[{"data":1,"prerenderedAt":1231},["ShallowReactive",2],{"navigation:en":3,"\u002Fblog\u002Fmulti-tenancy-rbac-seguranca-saas":129},[4],{"title":5,"path":6,"stem":7,"children":8,"icon":10},"Nuxfire Docs","\u002Fdocs","docs\u002F0.index",[9,11],{"title":5,"path":6,"stem":7,"icon":10},"i-ph-squares-four-duotone",{"title":12,"path":13,"stem":14,"children":15,"icon":18},"Flow Videos","\u002Fdocs\u002Fflow-videos","docs\u002Fflow-videos\u002F0.index",[16,19,36,67,93],{"title":17,"path":13,"stem":14,"icon":18},"Serelius Flow Video","i-ph-video-camera-duotone",{"title":20,"icon":21,"path":22,"stem":23,"children":24,"page":35},"Getting Started","i-ph-rocket-launch-duotone","\u002Fdocs\u002Fflow-videos\u002Fgetting-started","docs\u002Fflow-videos\u002F1.getting-started",[25,30],{"title":26,"path":27,"stem":28,"icon":29},"Overview","\u002Fdocs\u002Fflow-videos\u002Fgetting-started\u002Foverview","docs\u002Fflow-videos\u002F1.getting-started\u002F0.overview","i-ph-play-circle-duotone",{"title":31,"path":32,"stem":33,"icon":34},"Global Features","\u002Fdocs\u002Fflow-videos\u002Fgetting-started\u002Fglobal-features","docs\u002Fflow-videos\u002F1.getting-started\u002F1.global-features","i-ph-gear-six-duotone",false,{"title":37,"icon":38,"path":39,"stem":40,"children":41,"page":35},"Pipeline","i-ph-film-strip-duotone","\u002Fdocs\u002Fflow-videos\u002Fpipeline","docs\u002Fflow-videos\u002F2.pipeline",[42,47,52,57,62],{"title":43,"path":44,"stem":45,"icon":46},"Phase 1 — Script","\u002Fdocs\u002Fflow-videos\u002Fpipeline\u002Fscript","docs\u002Fflow-videos\u002F2.pipeline\u002F1.script","i-ph-scroll-duotone",{"title":48,"path":49,"stem":50,"icon":51},"Phase 2 — Style & Identity","\u002Fdocs\u002Fflow-videos\u002Fpipeline\u002Fstyle-and-identity","docs\u002Fflow-videos\u002F2.pipeline\u002F2.style-and-identity","i-ph-palette-duotone",{"title":53,"path":54,"stem":55,"icon":56},"Phase 3 — Storyboard","\u002Fdocs\u002Fflow-videos\u002Fpipeline\u002Fstoryboard","docs\u002Fflow-videos\u002F2.pipeline\u002F3.storyboard","i-ph-image-duotone",{"title":58,"path":59,"stem":60,"icon":61},"Phase 4 — Videos","\u002Fdocs\u002Fflow-videos\u002Fpipeline\u002Fvideos","docs\u002Fflow-videos\u002F2.pipeline\u002F4.videos","i-ph-video-duotone",{"title":63,"path":64,"stem":65,"icon":66},"Phase 5 — Final Assembly","\u002Fdocs\u002Fflow-videos\u002Fpipeline\u002Ffinal-assembly","docs\u002Fflow-videos\u002F2.pipeline\u002F5.final-assembly","i-ph-scissors-duotone",{"title":68,"icon":69,"path":70,"stem":71,"children":72,"page":35},"Settings & Models","i-ph-sliders-duotone","\u002Fdocs\u002Fflow-videos\u002Fsettings-and-models","docs\u002Fflow-videos\u002F3.settings-and-models",[73,78,83,88],{"title":74,"path":75,"stem":76,"icon":77},"Settings","\u002Fdocs\u002Fflow-videos\u002Fsettings-and-models\u002Fsettings","docs\u002Fflow-videos\u002F3.settings-and-models\u002F0.settings","i-ph-sliders-horizontal-duotone",{"title":79,"path":80,"stem":81,"icon":82},"AI Models & Limits","\u002Fdocs\u002Fflow-videos\u002Fsettings-and-models\u002Fmodels-and-limits","docs\u002Fflow-videos\u002F3.settings-and-models\u002F1.models-and-limits","i-ph-cpu-duotone",{"title":84,"path":85,"stem":86,"icon":87},"Option Catalogs","\u002Fdocs\u002Fflow-videos\u002Fsettings-and-models\u002Fcatalogs","docs\u002Fflow-videos\u002F3.settings-and-models\u002F2.catalogs","i-ph-list-bullets-duotone",{"title":89,"path":90,"stem":91,"icon":92},"Pipeline Calibration","\u002Fdocs\u002Fflow-videos\u002Fsettings-and-models\u002Fcalibration","docs\u002Fflow-videos\u002F3.settings-and-models\u002F3.calibration","i-ph-gauge-duotone",{"title":94,"icon":95,"path":96,"stem":97,"children":98,"page":35},"Reference","i-ph-book-open-duotone","\u002Fdocs\u002Fflow-videos\u002Freference","docs\u002Fflow-videos\u002F4.reference",[99,104,109,114,119,124],{"title":100,"path":101,"stem":102,"icon":103},"Delivered Files","\u002Fdocs\u002Fflow-videos\u002Freference\u002Fdelivered-files","docs\u002Fflow-videos\u002F4.reference\u002F0.delivered-files","i-ph-folder-duotone",{"title":105,"path":106,"stem":107,"icon":108},"Supported Video Types","\u002Fdocs\u002Fflow-videos\u002Freference\u002Fvideo-types","docs\u002Fflow-videos\u002F4.reference\u002F1.video-types","i-ph-television-duotone",{"title":110,"path":111,"stem":112,"icon":113},"What the Tool Does NOT Do","\u002Fdocs\u002Fflow-videos\u002Freference\u002Flimitations","docs\u002Fflow-videos\u002F4.reference\u002F2.limitations","i-ph-prohibit-duotone",{"title":115,"path":116,"stem":117,"icon":118},"Business Models","\u002Fdocs\u002Fflow-videos\u002Freference\u002Fbusiness-models","docs\u002Fflow-videos\u002F4.reference\u002F3.business-models","i-ph-briefcase-duotone",{"title":120,"path":121,"stem":122,"icon":123},"FAQ & Glossary","\u002Fdocs\u002Fflow-videos\u002Freference\u002Ffaq-and-glossary","docs\u002Fflow-videos\u002F4.reference\u002F4.faq-and-glossary","i-ph-question-duotone",{"title":125,"path":126,"stem":127,"icon":128},"Technical and Architectural Audit","\u002Fdocs\u002Fflow-videos\u002Freference\u002Faudit","docs\u002Fflow-videos\u002F4.reference\u002F5.audit","i-ph-shield-check-duotone",{"id":130,"title":131,"author":132,"authorType":133,"body":134,"category":1210,"date":1211,"description":1212,"extension":1213,"image":1214,"lang":1215,"meta":1216,"navigation":450,"ogImage":1217,"path":1218,"readTime":1219,"seo":1220,"stem":1221,"tags":1222,"translations":1226,"updated":133,"__hash__":1230},"blog\u002Fblog\u002Fmulti-tenancy-rbac-seguranca-saas.md","Multi-Tenancy Corporativo e RBAC Granular no PostgreSQL com Drizzle ORM","Equipe Nuxfire",null,{"type":135,"value":136,"toc":1204},"minimark",[137,150,153,172,175,180,183,203,206,730,732,736,742,1139,1141,1145,1148,1172,1174,1178,1188,1191,1200],[138,139,140,141,145,146,149],"p",{},"O maior divisor de águas entre um projeto amador e um produto de software corporativo pronto para vendas B2B é a segurança do ",[142,143,144],"strong",{},"multi-tenancy"," e do ",[142,147,148],{},"RBAC (Role-Based Access Control)",".",[138,151,152],{},"Se um cliente corporativo descobrir que um usuário de outra empresa conseguiu visualizar dados do seu workspace, a reputação da sua startup é destruída no primeiro dia.",[138,154,155,156,160,161,164,165,168,169,149],{},"Neste artigo, mostramos como a camada ",[157,158,159],"code",{},"layers\u002Fteams"," do ",[142,162,163],{},"Nuxfire"," implementa isolamento de dados à prova de falhas com ",[142,166,167],{},"PostgreSQL"," e ",[142,170,171],{},"Drizzle ORM",[173,174],"hr",{},[176,177,179],"h2",{"id":178},"modelagem-de-dados-o-tenant-id-em-cada-tabela","Modelagem de Dados: O Tenant Id em Cada Tabela",[138,181,182],{},"Existem três formas clássicas de multi-tenancy:",[184,185,186,190,193],"ol",{},[187,188,189],"li",{},"Banco de dados separado por cliente (caro e difícil de manter migrações).",[187,191,192],{},"Schema separado por cliente (limitações de conexões e complexidade no ORM).",[187,194,195,202],{},[142,196,197,198,201],{},"Tabelas compartilhadas com coluna discriminadora (",[157,199,200],{},"team_id",")"," — O padrão moderno da indústria adotado por gigantes como Stripe, Slack e Linear.",[138,204,205],{},"No Nuxfire, todas as entidades pertencem a uma equipe:",[207,208,213],"pre",{"className":209,"code":210,"language":211,"meta":212,"style":212},"language-typescript shiki shiki-themes material-theme-lighter github-light github-dark monokai","\u002F\u002F layers\u002Fteams\u002Fserver\u002Fdatabase\u002Fschema.ts\nexport const teams = pgTable(\"teams\", {\n  id: text(\"id\").primaryKey(),\n  name: text(\"name\").notNull(),\n  slug: text(\"slug\").notNull().unique(),\n  ownerId: text(\"owner_id\").notNull(),\n  createdAt: timestamp(\"created_at\").defaultNow().notNull()\n});\n\nexport const teamMembers = pgTable(\"team_members\", {\n  id: text(\"id\").primaryKey(),\n  teamId: text(\"team_id\").notNull().references(() => teams.id, { onDelete: \"cascade\" }),\n  userId: text(\"user_id\").notNull(),\n  role: text(\"role\", { enum: [\"OWNER\", \"ADMIN\", \"MEMBER\"] }).notNull().default(\"MEMBER\"),\n  createdAt: timestamp(\"created_at\").defaultNow().notNull()\n});\n","typescript","",[157,214,215,224,268,303,333,369,398,434,445,452,479,506,575,604,690,721],{"__ignoreMap":212},[216,217,220],"span",{"class":218,"line":219},"line",1,[216,221,223],{"class":222},"ss7Ak","\u002F\u002F layers\u002Fteams\u002Fserver\u002Fdatabase\u002Fschema.ts\n",[216,225,227,231,235,239,243,247,251,255,259,261,265],{"class":218,"line":226},2,[216,228,230],{"class":229},"sRxSC","export",[216,232,234],{"class":233},"srJo8"," const",[216,236,238],{"class":237},"s91G_"," teams",[216,240,242],{"class":241},"sGXK2"," =",[216,244,246],{"class":245},"sD0ED"," pgTable",[216,248,250],{"class":249},"ss--_","(",[216,252,254],{"class":253},"siCPE","\"",[216,256,258],{"class":257},"sLACW","teams",[216,260,254],{"class":253},[216,262,264],{"class":263},"swvn1",",",[216,266,267],{"class":263}," {\n",[216,269,271,275,278,281,283,285,288,290,292,294,297,300],{"class":218,"line":270},3,[216,272,274],{"class":273},"squCx","  id",[216,276,277],{"class":263},":",[216,279,280],{"class":245}," text",[216,282,250],{"class":249},[216,284,254],{"class":253},[216,286,287],{"class":257},"id",[216,289,254],{"class":253},[216,291,201],{"class":249},[216,293,149],{"class":263},[216,295,296],{"class":245},"primaryKey",[216,298,299],{"class":249},"()",[216,301,302],{"class":263},",\n",[216,304,306,309,311,313,315,317,320,322,324,326,329,331],{"class":218,"line":305},4,[216,307,308],{"class":273},"  name",[216,310,277],{"class":263},[216,312,280],{"class":245},[216,314,250],{"class":249},[216,316,254],{"class":253},[216,318,319],{"class":257},"name",[216,321,254],{"class":253},[216,323,201],{"class":249},[216,325,149],{"class":263},[216,327,328],{"class":245},"notNull",[216,330,299],{"class":249},[216,332,302],{"class":263},[216,334,336,339,341,343,345,347,350,352,354,356,358,360,362,365,367],{"class":218,"line":335},5,[216,337,338],{"class":273},"  slug",[216,340,277],{"class":263},[216,342,280],{"class":245},[216,344,250],{"class":249},[216,346,254],{"class":253},[216,348,349],{"class":257},"slug",[216,351,254],{"class":253},[216,353,201],{"class":249},[216,355,149],{"class":263},[216,357,328],{"class":245},[216,359,299],{"class":249},[216,361,149],{"class":263},[216,363,364],{"class":245},"unique",[216,366,299],{"class":249},[216,368,302],{"class":263},[216,370,372,375,377,379,381,383,386,388,390,392,394,396],{"class":218,"line":371},6,[216,373,374],{"class":273},"  ownerId",[216,376,277],{"class":263},[216,378,280],{"class":245},[216,380,250],{"class":249},[216,382,254],{"class":253},[216,384,385],{"class":257},"owner_id",[216,387,254],{"class":253},[216,389,201],{"class":249},[216,391,149],{"class":263},[216,393,328],{"class":245},[216,395,299],{"class":249},[216,397,302],{"class":263},[216,399,401,404,406,409,411,413,416,418,420,422,425,427,429,431],{"class":218,"line":400},7,[216,402,403],{"class":273},"  createdAt",[216,405,277],{"class":263},[216,407,408],{"class":245}," timestamp",[216,410,250],{"class":249},[216,412,254],{"class":253},[216,414,415],{"class":257},"created_at",[216,417,254],{"class":253},[216,419,201],{"class":249},[216,421,149],{"class":263},[216,423,424],{"class":245},"defaultNow",[216,426,299],{"class":249},[216,428,149],{"class":263},[216,430,328],{"class":245},[216,432,433],{"class":249},"()\n",[216,435,437,440,442],{"class":218,"line":436},8,[216,438,439],{"class":263},"}",[216,441,201],{"class":249},[216,443,444],{"class":263},";\n",[216,446,448],{"class":218,"line":447},9,[216,449,451],{"emptyLinePlaceholder":450},true,"\n",[216,453,455,457,459,462,464,466,468,470,473,475,477],{"class":218,"line":454},10,[216,456,230],{"class":229},[216,458,234],{"class":233},[216,460,461],{"class":237}," teamMembers",[216,463,242],{"class":241},[216,465,246],{"class":245},[216,467,250],{"class":249},[216,469,254],{"class":253},[216,471,472],{"class":257},"team_members",[216,474,254],{"class":253},[216,476,264],{"class":263},[216,478,267],{"class":263},[216,480,482,484,486,488,490,492,494,496,498,500,502,504],{"class":218,"line":481},11,[216,483,274],{"class":273},[216,485,277],{"class":263},[216,487,280],{"class":245},[216,489,250],{"class":249},[216,491,254],{"class":253},[216,493,287],{"class":257},[216,495,254],{"class":253},[216,497,201],{"class":249},[216,499,149],{"class":263},[216,501,296],{"class":245},[216,503,299],{"class":249},[216,505,302],{"class":263},[216,507,509,512,514,516,518,520,522,524,526,528,530,532,534,537,539,541,544,546,548,550,552,555,558,560,563,566,568,571,573],{"class":218,"line":508},12,[216,510,511],{"class":273},"  teamId",[216,513,277],{"class":263},[216,515,280],{"class":245},[216,517,250],{"class":249},[216,519,254],{"class":253},[216,521,200],{"class":257},[216,523,254],{"class":253},[216,525,201],{"class":249},[216,527,149],{"class":263},[216,529,328],{"class":245},[216,531,299],{"class":249},[216,533,149],{"class":263},[216,535,536],{"class":245},"references",[216,538,250],{"class":249},[216,540,299],{"class":263},[216,542,543],{"class":233}," =>",[216,545,238],{"class":249},[216,547,149],{"class":263},[216,549,287],{"class":249},[216,551,264],{"class":263},[216,553,554],{"class":263}," {",[216,556,557],{"class":273}," onDelete",[216,559,277],{"class":263},[216,561,562],{"class":253}," \"",[216,564,565],{"class":257},"cascade",[216,567,254],{"class":253},[216,569,570],{"class":263}," }",[216,572,201],{"class":249},[216,574,302],{"class":263},[216,576,578,581,583,585,587,589,592,594,596,598,600,602],{"class":218,"line":577},13,[216,579,580],{"class":273},"  userId",[216,582,277],{"class":263},[216,584,280],{"class":245},[216,586,250],{"class":249},[216,588,254],{"class":253},[216,590,591],{"class":257},"user_id",[216,593,254],{"class":253},[216,595,201],{"class":249},[216,597,149],{"class":263},[216,599,328],{"class":245},[216,601,299],{"class":249},[216,603,302],{"class":263},[216,605,607,610,612,614,616,618,621,623,625,627,630,632,635,637,640,642,644,646,649,651,653,655,658,660,663,665,667,669,671,673,675,678,680,682,684,686,688],{"class":218,"line":606},14,[216,608,609],{"class":273},"  role",[216,611,277],{"class":263},[216,613,280],{"class":245},[216,615,250],{"class":249},[216,617,254],{"class":253},[216,619,620],{"class":257},"role",[216,622,254],{"class":253},[216,624,264],{"class":263},[216,626,554],{"class":263},[216,628,629],{"class":273}," enum",[216,631,277],{"class":263},[216,633,634],{"class":249}," [",[216,636,254],{"class":253},[216,638,639],{"class":257},"OWNER",[216,641,254],{"class":253},[216,643,264],{"class":263},[216,645,562],{"class":253},[216,647,648],{"class":257},"ADMIN",[216,650,254],{"class":253},[216,652,264],{"class":263},[216,654,562],{"class":253},[216,656,657],{"class":257},"MEMBER",[216,659,254],{"class":253},[216,661,662],{"class":249},"] ",[216,664,439],{"class":263},[216,666,201],{"class":249},[216,668,149],{"class":263},[216,670,328],{"class":245},[216,672,299],{"class":249},[216,674,149],{"class":263},[216,676,677],{"class":245},"default",[216,679,250],{"class":249},[216,681,254],{"class":253},[216,683,657],{"class":257},[216,685,254],{"class":253},[216,687,201],{"class":249},[216,689,302],{"class":263},[216,691,693,695,697,699,701,703,705,707,709,711,713,715,717,719],{"class":218,"line":692},15,[216,694,403],{"class":273},[216,696,277],{"class":263},[216,698,408],{"class":245},[216,700,250],{"class":249},[216,702,254],{"class":253},[216,704,415],{"class":257},[216,706,254],{"class":253},[216,708,201],{"class":249},[216,710,149],{"class":263},[216,712,424],{"class":245},[216,714,299],{"class":249},[216,716,149],{"class":263},[216,718,328],{"class":245},[216,720,433],{"class":249},[216,722,724,726,728],{"class":218,"line":723},16,[216,725,439],{"class":263},[216,727,201],{"class":249},[216,729,444],{"class":263},[173,731],{},[176,733,735],{"id":734},"middleware-e-procedures-trpc-autenticadas","Middleware e Procedures tRPC Autenticadas",[138,737,738,739,741],{},"Para garantir que nenhum desenvolvedor esqueça de filtrar pelo ",[157,740,200],{},", as requisições passam por procedimentos tRPC protegidos:",[207,743,745],{"className":209,"code":744,"language":211,"meta":212,"style":212},"export const teamProcedure = protectedProcedure\n  .input(z.object({ teamId: z.string() }))\n  .use(async ({ ctx, input, next }) => {\n    const membership = await ctx.db.query.teamMembers.findFirst({\n      where: and(\n        eq(teamMembers.teamId, input.teamId),\n        eq(teamMembers.userId, ctx.user.id)\n      )\n    });\n\n    if (!membership) {\n      throw new TRPCError({\n        code: \"FORBIDDEN\",\n        message: \"Você não possui acesso a este workspace.\"\n      });\n    }\n\n    return next({\n      ctx: {\n        ...ctx,\n        teamId: input.teamId,\n        role: membership.role\n      }\n    });\n  });\n",[157,746,747,761,803,840,880,893,919,948,953,962,966,985,1000,1016,1031,1040,1045,1050,1062,1072,1083,1099,1114,1120,1129],{"__ignoreMap":212},[216,748,749,751,753,756,758],{"class":218,"line":219},[216,750,230],{"class":229},[216,752,234],{"class":233},[216,754,755],{"class":237}," teamProcedure",[216,757,242],{"class":241},[216,759,760],{"class":249}," protectedProcedure\n",[216,762,763,766,769,772,774,777,779,782,785,787,790,792,795,798,800],{"class":218,"line":226},[216,764,765],{"class":263},"  .",[216,767,768],{"class":245},"input",[216,770,771],{"class":249},"(z",[216,773,149],{"class":263},[216,775,776],{"class":245},"object",[216,778,250],{"class":249},[216,780,781],{"class":263},"{",[216,783,784],{"class":273}," teamId",[216,786,277],{"class":263},[216,788,789],{"class":249}," z",[216,791,149],{"class":263},[216,793,794],{"class":245},"string",[216,796,797],{"class":249},"() ",[216,799,439],{"class":263},[216,801,802],{"class":249},"))\n",[216,804,805,807,810,812,816,819,823,825,828,830,833,836,838],{"class":218,"line":270},[216,806,765],{"class":263},[216,808,809],{"class":245},"use",[216,811,250],{"class":249},[216,813,815],{"class":814},"sTNss","async",[216,817,818],{"class":263}," ({",[216,820,822],{"class":821},"sQgqH"," ctx",[216,824,264],{"class":263},[216,826,827],{"class":821}," input",[216,829,264],{"class":263},[216,831,832],{"class":821}," next",[216,834,835],{"class":263}," })",[216,837,543],{"class":233},[216,839,267],{"class":263},[216,841,842,845,848,850,853,855,857,860,862,865,867,870,872,875,877],{"class":218,"line":305},[216,843,844],{"class":233},"    const",[216,846,847],{"class":237}," membership",[216,849,242],{"class":241},[216,851,852],{"class":229}," await",[216,854,822],{"class":249},[216,856,149],{"class":263},[216,858,859],{"class":249},"db",[216,861,149],{"class":263},[216,863,864],{"class":249},"query",[216,866,149],{"class":263},[216,868,869],{"class":249},"teamMembers",[216,871,149],{"class":263},[216,873,874],{"class":245},"findFirst",[216,876,250],{"class":273},[216,878,879],{"class":263},"{\n",[216,881,882,885,887,890],{"class":218,"line":335},[216,883,884],{"class":273},"      where",[216,886,277],{"class":263},[216,888,889],{"class":245}," and",[216,891,892],{"class":273},"(\n",[216,894,895,898,900,902,904,907,909,911,913,915,917],{"class":218,"line":371},[216,896,897],{"class":245},"        eq",[216,899,250],{"class":273},[216,901,869],{"class":249},[216,903,149],{"class":263},[216,905,906],{"class":249},"teamId",[216,908,264],{"class":263},[216,910,827],{"class":249},[216,912,149],{"class":263},[216,914,906],{"class":249},[216,916,201],{"class":273},[216,918,302],{"class":263},[216,920,921,923,925,927,929,932,934,936,938,941,943,945],{"class":218,"line":400},[216,922,897],{"class":245},[216,924,250],{"class":273},[216,926,869],{"class":249},[216,928,149],{"class":263},[216,930,931],{"class":249},"userId",[216,933,264],{"class":263},[216,935,822],{"class":249},[216,937,149],{"class":263},[216,939,940],{"class":249},"user",[216,942,149],{"class":263},[216,944,287],{"class":249},[216,946,947],{"class":273},")\n",[216,949,950],{"class":218,"line":436},[216,951,952],{"class":273},"      )\n",[216,954,955,958,960],{"class":218,"line":447},[216,956,957],{"class":263},"    }",[216,959,201],{"class":273},[216,961,444],{"class":263},[216,963,964],{"class":218,"line":454},[216,965,451],{"emptyLinePlaceholder":450},[216,967,968,971,974,977,980,983],{"class":218,"line":481},[216,969,970],{"class":229},"    if",[216,972,973],{"class":273}," (",[216,975,976],{"class":241},"!",[216,978,979],{"class":249},"membership",[216,981,982],{"class":273},") ",[216,984,879],{"class":263},[216,986,987,990,993,996,998],{"class":218,"line":508},[216,988,989],{"class":229},"      throw",[216,991,992],{"class":241}," new",[216,994,995],{"class":245}," TRPCError",[216,997,250],{"class":273},[216,999,879],{"class":263},[216,1001,1002,1005,1007,1009,1012,1014],{"class":218,"line":577},[216,1003,1004],{"class":273},"        code",[216,1006,277],{"class":263},[216,1008,562],{"class":253},[216,1010,1011],{"class":257},"FORBIDDEN",[216,1013,254],{"class":253},[216,1015,302],{"class":263},[216,1017,1018,1021,1023,1025,1028],{"class":218,"line":606},[216,1019,1020],{"class":273},"        message",[216,1022,277],{"class":263},[216,1024,562],{"class":253},[216,1026,1027],{"class":257},"Você não possui acesso a este workspace.",[216,1029,1030],{"class":253},"\"\n",[216,1032,1033,1036,1038],{"class":218,"line":692},[216,1034,1035],{"class":263},"      }",[216,1037,201],{"class":273},[216,1039,444],{"class":263},[216,1041,1042],{"class":218,"line":723},[216,1043,1044],{"class":263},"    }\n",[216,1046,1048],{"class":218,"line":1047},17,[216,1049,451],{"emptyLinePlaceholder":450},[216,1051,1053,1056,1058,1060],{"class":218,"line":1052},18,[216,1054,1055],{"class":229},"    return",[216,1057,832],{"class":245},[216,1059,250],{"class":273},[216,1061,879],{"class":263},[216,1063,1065,1068,1070],{"class":218,"line":1064},19,[216,1066,1067],{"class":273},"      ctx",[216,1069,277],{"class":263},[216,1071,267],{"class":263},[216,1073,1075,1078,1081],{"class":218,"line":1074},20,[216,1076,1077],{"class":241},"        ...",[216,1079,1080],{"class":249},"ctx",[216,1082,302],{"class":263},[216,1084,1086,1089,1091,1093,1095,1097],{"class":218,"line":1085},21,[216,1087,1088],{"class":273},"        teamId",[216,1090,277],{"class":263},[216,1092,827],{"class":249},[216,1094,149],{"class":263},[216,1096,906],{"class":249},[216,1098,302],{"class":263},[216,1100,1102,1105,1107,1109,1111],{"class":218,"line":1101},22,[216,1103,1104],{"class":273},"        role",[216,1106,277],{"class":263},[216,1108,847],{"class":249},[216,1110,149],{"class":263},[216,1112,1113],{"class":249},"role\n",[216,1115,1117],{"class":218,"line":1116},23,[216,1118,1119],{"class":263},"      }\n",[216,1121,1123,1125,1127],{"class":218,"line":1122},24,[216,1124,957],{"class":263},[216,1126,201],{"class":273},[216,1128,444],{"class":263},[216,1130,1132,1135,1137],{"class":218,"line":1131},25,[216,1133,1134],{"class":263},"  }",[216,1136,201],{"class":249},[216,1138,444],{"class":263},[173,1140],{},[176,1142,1144],{"id":1143},"fluxo-de-convites-por-e-mail-com-token-criptografado","Fluxo de Convites por E-mail com Token Criptografado",[138,1146,1147],{},"Convites para novos membros seguem um fluxo seguro de dupla confirmação:",[184,1149,1150,1159,1162,1169],{},[187,1151,1152,1153,1155,1156,1158],{},"O administrador da equipe insere o e-mail do colaborador e seleciona o papel (",[157,1154,648],{}," ou ",[157,1157,657],{},").",[187,1160,1161],{},"O sistema gera um token aleatório criptograficamente seguro com expiração de 7 dias.",[187,1163,1164,1165,1168],{},"Um e-mail elegante construído com ",[142,1166,1167],{},"React Email"," é disparado via fila assíncrona.",[187,1170,1171],{},"Ao clicar no link, o usuário é direcionado para a tela de aceite ou onboarding, associando a conta à equipe instantaneamente.",[173,1173],{},[176,1175,1177],{"id":1176},"logs-de-auditoria-para-conformidade-corporativa","Logs de Auditoria para Conformidade Corporativa",[138,1179,1180,1181,973,1184,1187],{},"Para clientes B2B exigentes, o Nuxfire já inclui uma tabela dedicada de ",[142,1182,1183],{},"Audit Logs",[157,1185,1186],{},"\u002Fadmin\u002Faudit-log","). Todas as alterações críticas — mudanças de permissões, remoção de membros, trocas de plano e downloads de dados — são registradas de forma imutável com timestamp, IP e identificador do autor.",[138,1189,1190],{},"Construa seu SaaS sobre uma fundação que atende aos mais altos padrões de segurança corporativa desde o primeiro commit.",[138,1192,1193],{},[1194,1195,1197],"a",{"href":1196},"\u002Fpt-br#pricing",[142,1198,1199],{},"Adquira sua licença do Nuxfire e garanta acesso imediato ao código e ao Grupo VIP →",[1201,1202,1203],"style",{},"html pre.shiki code .ss7Ak, html code.shiki .ss7Ak{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit;--shiki-sepia:#88846F;--shiki-sepia-font-style:inherit}html pre.shiki code .sRxSC, html code.shiki .sRxSC{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit;--shiki-sepia:#F92672;--shiki-sepia-font-style:inherit}html pre.shiki code .srJo8, html code.shiki .srJo8{--shiki-light:#9C3EDA;--shiki-light-font-style:inherit;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit;--shiki-sepia:#66D9EF;--shiki-sepia-font-style:italic}html pre.shiki code .s91G_, html code.shiki .s91G_{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF;--shiki-sepia:#F8F8F2}html pre.shiki code .sGXK2, html code.shiki .sGXK2{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#F92672}html pre.shiki code .sD0ED, html code.shiki .sD0ED{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0;--shiki-sepia:#A6E22E}html pre.shiki code .ss--_, html code.shiki .ss--_{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#F8F8F2}html pre.shiki code .siCPE, html code.shiki .siCPE{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#E6DB74}html pre.shiki code .sLACW, html code.shiki .sLACW{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF;--shiki-sepia:#E6DB74}html pre.shiki code .swvn1, html code.shiki .swvn1{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#F8F8F2}html pre.shiki code .squCx, html code.shiki .squCx{--shiki-light:#E53935;--shiki-default:#24292E;--shiki-dark:#E1E4E8;--shiki-sepia:#F8F8F2}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html .sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html.sepia .shiki span {color: var(--shiki-sepia);background: var(--shiki-sepia-bg);font-style: var(--shiki-sepia-font-style);font-weight: var(--shiki-sepia-font-weight);text-decoration: var(--shiki-sepia-text-decoration);}html pre.shiki code .sTNss, html code.shiki .sTNss{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583;--shiki-sepia:#F92672}html pre.shiki code .sQgqH, html code.shiki .sQgqH{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#E36209;--shiki-default-font-style:inherit;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit;--shiki-sepia:#FD971F;--shiki-sepia-font-style:italic}",{"title":212,"searchDepth":226,"depth":226,"links":1205},[1206,1207,1208,1209],{"id":178,"depth":226,"text":179},{"id":734,"depth":226,"text":735},{"id":1143,"depth":226,"text":1144},{"id":1176,"depth":226,"text":1177},"Segurança & Banco de Dados","2026-09-15","Como implementar isolamento estrito entre equipes, permissões granulares e convites seguros por token sem brechas de segurança no seu SaaS B2B.","md","\u002Fimages\u002Fblog\u002Fmulti-tenancy-rbac-postgresql.svg","pt-br",{},"\u002Fimages\u002Fblog\u002Fmulti-tenancy-rbac-postgresql.png","\u002Fblog\u002Fmulti-tenancy-rbac-seguranca-saas","9 min",{"title":131,"description":1212},"blog\u002Fmulti-tenancy-rbac-seguranca-saas",[1223,1224,167,171,1225],"Multi-Tenancy","RBAC","Segurança",{"en":1227,"pt-br":1228,"es":1229},"multi-tenancy-rbac-saas-security","multi-tenancy-rbac-seguranca-saas","multi-tenancy-rbac-seguridad-saas","aqhB0STzIYzhs34XIm6BPQoCM7JmcIGlfkNoyxDQOIQ",1794055339961]