[{"data":1,"prerenderedAt":2529},["ShallowReactive",2],{"article-fr-\u002Fsecurity\u002Fauthz\u002Fzanzibar-explained":3,"article-sibling-fr-\u002Fsecurity\u002Fauthz\u002Fzanzibar-explained":1337,"surround-fr-\u002Fsecurity\u002Fauthz\u002Fzanzibar-explained":2496,"related-fr-\u002Fsecurity\u002Fauthz\u002Fzanzibar-explained":2503},{"id":4,"title":5,"body":6,"date":1318,"description":1319,"draft":1320,"extension":1321,"img":1322,"meta":1323,"navigation":398,"path":1324,"seo":1325,"slug":1326,"stem":1327,"tags":1328,"topics":1332,"__hash__":1336},"content\u002F3.security\u002F2.authz\u002F1.zanzibar-explained.fr.md","Zanzibar demystifie : comment Google repond a 'cet utilisateur a-t-il le droit ?'",{"type":7,"value":8,"toc":1306},"minimark",[9,23,32,39,44,47,225,228,269,276,280,287,295,314,321,332,338,356,360,374,377,434,459,462,596,599,605,608,612,615,659,676,795,808,812,827,876,890,1010,1024,1028,1034,1037,1052,1059,1066,1070,1093,1097,1100,1123,1142,1149,1153,1156,1235,1249,1253,1299,1302],[10,11,12,13,17,18,22],"p",{},"Toute application jamais ecrite doit repondre a une question, encore et encore : ",[14,15,16],"strong",{},"cet utilisateur peut-il faire cette action sur cette ressource ?"," Alice peut-elle lire ce document. Bob peut-il supprimer ce commentaire. Ce service peut-il appeler cet endpoint. On dirait la ligne de code la plus ennuyeuse du batiment, un simple ",[19,20,21],"code",{},"if",".",[10,24,25,26,28,29,31],{},"Puis le produit grandit. Le document vit dans un dossier, et l'acces au dossier doit se propager. Le document est partage avec une equipe, l'equipe a des sous-equipes, et les gens arrivent et partent. Quelqu'un au support doit repondre a \"pourquoi Bob voit-il ce fichier ?\" et personne ne le peut, car la reponse est eparpillee dans six services ayant chacun son propre ",[19,27,21],{},". Puis un utilisateur est retire d'un groupe et, pendant les trente secondes suivantes, un cache perime quelque part repond encore oui. Ce n'est plus un ",[19,30,21],{}," ennuyeux. C'est un probleme de systemes distribues deguise en booleen.",[10,33,34,35,38],{},"Google a rencontre cela a une echelle que personne d'autre n'avait : une seule question d'autorisation que Docs, Drive, Photos, Calendar, YouTube et Cloud devaient tous poser, correctement, des milliards de fois par jour, en moins de dix millisecondes, partout sur Terre. Leur reponse fut ",[14,36,37],{},"Zanzibar",", decrite dans un article de 2019 qui est discretement devenu le design d'autorisation le plus influent de la decennie. On parcourt ici ce que c'est vraiment, pourquoi chaque partie difficile est difficile, et comment vous pouvez utiliser le meme modele aujourd'hui via des heritiers open source comme OpenFGA, sans faire tourner l'infrastructure de Google.",[40,41,43],"h2",{"id":42},"la-question-qui-parait-simple-et-ne-lest-pas","La question qui parait simple et ne l'est pas",[10,45,46],{},"Commencons par la version que tout le monde ecrit en premier. Les permissions vivent a cote de la ressource, et on les verifie en ligne.",[48,49,54],"pre",{"className":50,"code":51,"language":52,"meta":53,"style":53},"language-go shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","func canView(user User, doc Document) bool {\n    if doc.OwnerID == user.ID {\n        return true\n    }\n    for _, id := range doc.SharedWith {\n        if id == user.ID {\n            return true\n        }\n    }\n    return false\n}\n","go","",[19,55,56,99,128,138,144,173,191,199,205,210,219],{"__ignoreMap":53},[57,58,61,65,69,72,76,80,83,86,89,92,96],"span",{"class":59,"line":60},"line",1,[57,62,64],{"class":63},"sMK4o","func",[57,66,68],{"class":67},"s2Zo4"," canView",[57,70,71],{"class":63},"(",[57,73,75],{"class":74},"sHdIc","user",[57,77,79],{"class":78},"sBMFI"," User",[57,81,82],{"class":63},",",[57,84,85],{"class":74}," doc",[57,87,88],{"class":78}," Document",[57,90,91],{"class":63},")",[57,93,95],{"class":94},"spNyl"," bool",[57,97,98],{"class":63}," {\n",[57,100,102,106,109,111,114,117,120,122,125],{"class":59,"line":101},2,[57,103,105],{"class":104},"s7zQu","    if",[57,107,85],{"class":108},"sTEyZ",[57,110,22],{"class":63},[57,112,113],{"class":108},"OwnerID ",[57,115,116],{"class":63},"==",[57,118,119],{"class":108}," user",[57,121,22],{"class":63},[57,123,124],{"class":108},"ID ",[57,126,127],{"class":63},"{\n",[57,129,131,134],{"class":59,"line":130},3,[57,132,133],{"class":104},"        return",[57,135,137],{"class":136},"sfNiH"," true\n",[57,139,141],{"class":59,"line":140},4,[57,142,143],{"class":63},"    }\n",[57,145,147,150,153,155,158,161,164,166,168,171],{"class":59,"line":146},5,[57,148,149],{"class":104},"    for",[57,151,152],{"class":108}," _",[57,154,82],{"class":63},[57,156,157],{"class":108}," id ",[57,159,160],{"class":63},":=",[57,162,163],{"class":104}," range",[57,165,85],{"class":108},[57,167,22],{"class":63},[57,169,170],{"class":108},"SharedWith ",[57,172,127],{"class":63},[57,174,176,179,181,183,185,187,189],{"class":59,"line":175},6,[57,177,178],{"class":104},"        if",[57,180,157],{"class":108},[57,182,116],{"class":63},[57,184,119],{"class":108},[57,186,22],{"class":63},[57,188,124],{"class":108},[57,190,127],{"class":63},[57,192,194,197],{"class":59,"line":193},7,[57,195,196],{"class":104},"            return",[57,198,137],{"class":136},[57,200,202],{"class":59,"line":201},8,[57,203,204],{"class":63},"        }\n",[57,206,208],{"class":59,"line":207},9,[57,209,143],{"class":63},[57,211,213,216],{"class":59,"line":212},10,[57,214,215],{"class":104},"    return",[57,217,218],{"class":136}," false\n",[57,220,222],{"class":59,"line":221},11,[57,223,224],{"class":63},"}\n",[10,226,227],{},"Le premier jour, ca va. Le probleme n'est pas que c'est faux, c'est que ca ne se compose pas, et l'autorisation est entierement une affaire de composition :",[229,230,231,242,251,257,263],"ul",{},[232,233,234,237,238,241],"li",{},[14,235,236],{},"Heritage."," Un document dans un dossier devrait heriter des lecteurs du dossier. Voila ",[19,239,240],{},"canView"," qui doit connaitre les dossiers. Les dossiers s'imbriquent, donc il lui faut de la recursion.",[232,243,244,247,248,250],{},[14,245,246],{},"Groupes."," On partage avec une equipe, pas avec une liste d'ID utilisateurs. Les equipes contiennent des equipes. Voila ",[19,249,240],{}," qui parcourt un graphe de groupes.",[232,252,253,256],{},[14,254,255],{},"Duplication."," Docs, Drive et Photos font chacun grandir leur propre copie de cette logique, et elles divergent. Un bug de permission dans l'un n'est pas un bug dans les autres, ce qui est pire, car on ne peut plus raisonner sur le systeme dans son ensemble.",[232,258,259,262],{},[14,260,261],{},"Audit."," \"Pourquoi Bob a-t-il l'acces ?\" n'a pas de reponse, car l'acces est un calcul disperse entre services et lignes de base, pas un fait qu'on peut consulter.",[232,264,265,268],{},[14,266,267],{},"Coherence."," Vous revoquez Bob et changez aussitot ce qu'il regardait. Si un cache sert l'ancienne reponse ne serait-ce qu'un instant, Bob voit du contenu qu'il ne devrait pas. L'article appelle cela le probleme du \"nouvel ennemi\" (new enemy), et c'est toute la raison pour laquelle Zanzibar est complique.",[10,270,271,272,275],{},"RBAC (roles) et ABAC (attributs) resolvent chacun une partie de cela et aucun ne resout la forme du probleme, car la vraie forme est un ",[14,273,274],{},"graphe"," : les utilisateurs sont lies a des groupes, les groupes a des dossiers, les dossiers a des documents, et un controle de permission est une question d'accessibilite dans ce graphe. Ce recadrage est l'idee entiere derriere Zanzibar.",[40,277,279],{"id":278},"lidee-centrale-les-permissions-sont-des-relations","L'idee centrale : les permissions sont des relations",[10,281,282,283,286],{},"Zanzibar stocke l'autorisation sous une seule sorte de fait uniforme appelee ",[14,284,285],{},"tuple de relation",". Lisez-en un a voix haute et c'est une phrase :",[48,288,293],{"className":289,"code":291,"language":292},[290],"language-text","document:readme#viewer@user:alice\n","text",[19,294,291],{"__ignoreMap":53},[10,296,297,298,301,302,305,306,309,310,313],{},"\"Sur l'objet ",[19,299,300],{},"document:readme",", la relation ",[19,303,304],{},"viewer"," inclut ",[19,307,308],{},"user:alice",".\" Alice peut voir le readme. Chaque permission de toute l'entreprise se reduit a un tas de ces tuples de la forme ",[19,311,312],{},"object#relation@subject",". Il n'y a pas de schema propre a chaque produit avec ses colonnes et ses tables de jointure ; il y a une seule table de relations, et un controle de permission est une requete de graphe dessus.",[10,315,316,317,320],{},"C'est le ",[14,318,319],{},"ReBAC",", controle d'acces base sur les relations (relationship-based access control). L'intuition est que \"role\" et \"attribut\" ne sont que des cas particuliers de \"relation\". Proprietaire est une relation. Appartenance est une relation. Dossier parent est une relation. Une fois que tout est le meme genre d'arete, un seul moteur peut repondre a chaque question, et l'audit vient gratuitement : \"pourquoi Bob a-t-il l'acces ?\" devient \"montre-moi le chemin de tuples de Bob vers ce document\", ce qui est une requete, pas une fouille archeologique.",[10,322,323,324,327,328,331],{},"Le sujet a droite du ",[19,325,326],{},"@"," peut lui-meme etre un ",[14,329,330],{},"ensemble"," d'utilisateurs, pas juste un seul, et c'est la que le modele tire sa puissance. Au lieu de nommer Alice, on pointe vers tous ceux qui sont membres d'un groupe :",[48,333,336],{"className":334,"code":335,"language":292},[290],"document:readme#viewer@group:engineering#member\n",[19,337,335],{"__ignoreMap":53},[10,339,340,341,344,345,348,349,352,353,355],{},"\"Tous ceux de la relation ",[19,342,343],{},"member"," de ",[19,346,347],{},"group:engineering"," sont lecteurs du readme.\" C'est un ",[14,350,351],{},"userset"," : une reference vers \"les membres de cette relation sur cet objet\". Les usersets sont la maniere dont les groupes, l'imbrication et l'heritage s'effondrent tous dans le meme format de tuple. Un groupe avec des sous-groupes est juste un tuple dont le sujet est le userset ",[19,354,343],{}," d'un autre groupe. Le graphe se construit tout seul.",[40,357,359],{"id":358},"le-modeliser-avec-openfga","Le modeliser avec OpenFGA",[10,361,362,363,366,367,370,371,22],{},"Zanzibar lui-meme est interne a Google, mais son modele est desormais un standard ouvert que vous pouvez faire tourner. ",[14,364,365],{},"OpenFGA"," (un projet CNCF, ne chez Auth0\u002FOkta) est l'implementation la plus utilisee, et son DSL rend le modele concret. Un modele a des ",[14,368,369],{},"types"," (les sortes d'objets), et chaque type declare des ",[14,372,373],{},"relations",[10,375,376],{},"Commencons par le modele utile le plus simple : des documents avec trois roles, ou chaque role implique le plus faible.",[48,378,382],{"className":379,"code":380,"language":381,"meta":53,"style":53},"language-dsl shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","model\n  schema 1.1\n\ntype user\n\ntype document\n  relations\n    define owner: [user]\n    define editor: [user] or owner\n    define viewer: [user] or editor\n","dsl",[19,383,384,389,394,400,405,409,414,419,424,429],{"__ignoreMap":53},[57,385,386],{"class":59,"line":60},[57,387,388],{},"model\n",[57,390,391],{"class":59,"line":101},[57,392,393],{},"  schema 1.1\n",[57,395,396],{"class":59,"line":130},[57,397,399],{"emptyLinePlaceholder":398},true,"\n",[57,401,402],{"class":59,"line":140},[57,403,404],{},"type user\n",[57,406,407],{"class":59,"line":146},[57,408,399],{"emptyLinePlaceholder":398},[57,410,411],{"class":59,"line":175},[57,412,413],{},"type document\n",[57,415,416],{"class":59,"line":193},[57,417,418],{},"  relations\n",[57,420,421],{"class":59,"line":201},[57,422,423],{},"    define owner: [user]\n",[57,425,426],{"class":59,"line":207},[57,427,428],{},"    define editor: [user] or owner\n",[57,430,431],{"class":59,"line":212},[57,432,433],{},"    define viewer: [user] or editor\n",[10,435,436,437,440,441,444,445,447,448,450,451,454,455,458],{},"Lisez les regles de reecriture a droite. ",[19,438,439],{},"editor"," est \"quiconque assigne directement editor, ",[14,442,443],{},"ou"," quiconque est owner\". ",[19,446,304],{}," est \"quiconque assigne directement viewer, ",[14,449,443],{}," quiconque est editor\". Donc les owners sont editors sont viewers, gratuitement, sans tuple duplique. Ce ",[19,452,453],{},"or"," est une ",[14,456,457],{},"union"," d'ensembles, la meme union que la config de Zanzibar appelle userset rewrite. Le moteur la calcule au moment du controle ; vous ne stockez jamais les tuples derives.",[10,460,461],{},"Maintenant les faits de permission reels, les tuples :",[48,463,467],{"className":464,"code":465,"language":466,"meta":53,"style":53},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","[\n  { \"user\": \"user:alice\",  \"relation\": \"owner\",  \"object\": \"document:readme\" },\n  { \"user\": \"user:bob\",   \"relation\": \"viewer\", \"object\": \"document:readme\" }\n]\n","json",[19,468,469,474,536,591],{"__ignoreMap":53},[57,470,471],{"class":59,"line":60},[57,472,473],{"class":63},"[\n",[57,475,476,479,482,484,487,490,492,495,497,499,502,505,507,509,511,514,516,518,520,523,525,527,529,531,533],{"class":59,"line":101},[57,477,478],{"class":63},"  {",[57,480,481],{"class":63}," \"",[57,483,75],{"class":94},[57,485,486],{"class":63},"\"",[57,488,489],{"class":63},":",[57,491,481],{"class":63},[57,493,308],{"class":494},"sfazB",[57,496,486],{"class":63},[57,498,82],{"class":63},[57,500,501],{"class":63},"  \"",[57,503,504],{"class":94},"relation",[57,506,486],{"class":63},[57,508,489],{"class":63},[57,510,481],{"class":63},[57,512,513],{"class":494},"owner",[57,515,486],{"class":63},[57,517,82],{"class":63},[57,519,501],{"class":63},[57,521,522],{"class":94},"object",[57,524,486],{"class":63},[57,526,489],{"class":63},[57,528,481],{"class":63},[57,530,300],{"class":494},[57,532,486],{"class":63},[57,534,535],{"class":63}," },\n",[57,537,538,540,542,544,546,548,550,553,555,557,560,562,564,566,568,570,572,574,576,578,580,582,584,586,588],{"class":59,"line":130},[57,539,478],{"class":63},[57,541,481],{"class":63},[57,543,75],{"class":94},[57,545,486],{"class":63},[57,547,489],{"class":63},[57,549,481],{"class":63},[57,551,552],{"class":494},"user:bob",[57,554,486],{"class":63},[57,556,82],{"class":63},[57,558,559],{"class":63},"   \"",[57,561,504],{"class":94},[57,563,486],{"class":63},[57,565,489],{"class":63},[57,567,481],{"class":63},[57,569,304],{"class":494},[57,571,486],{"class":63},[57,573,82],{"class":63},[57,575,481],{"class":63},[57,577,522],{"class":94},[57,579,486],{"class":63},[57,581,489],{"class":63},[57,583,481],{"class":63},[57,585,300],{"class":494},[57,587,486],{"class":63},[57,589,590],{"class":63}," }\n",[57,592,593],{"class":59,"line":140},[57,594,595],{"class":63},"]\n",[10,597,598],{},"Alice en est proprietaire, Bob peut le voir. Un controle donne :",[48,600,603],{"className":601,"code":602,"language":292},[290],"check(user:alice, viewer, document:readme)  →  allowed: true   \u002F\u002F owner ⇒ editor ⇒ viewer\ncheck(user:bob,  editor, document:readme)  →  allowed: false  \u002F\u002F seulement viewer direct\n",[19,604,602],{"__ignoreMap":53},[10,606,607],{},"Alice est lectrice bien qu'aucun tuple ne le dise, car le modele le derive. C'est toute l'astuce, en petit.",[40,609,611],{"id":610},"les-groupes-sans-jamais-retoucher-le-document","Les groupes, sans jamais retoucher le document",[10,613,614],{},"Ajoutons un type groupe et laissons un document etre partage avec les membres d'un groupe. C'est le userset de tout a l'heure, en syntaxe OpenFGA.",[48,616,618],{"className":379,"code":617,"language":381,"meta":53,"style":53},"type group\n  relations\n    define member: [user]\n\ntype document\n  relations\n    define owner: [user]\n    define editor: [user] or owner\n    define viewer: [user, group#member] or editor\n",[19,619,620,625,629,634,638,642,646,650,654],{"__ignoreMap":53},[57,621,622],{"class":59,"line":60},[57,623,624],{},"type group\n",[57,626,627],{"class":59,"line":101},[57,628,418],{},[57,630,631],{"class":59,"line":130},[57,632,633],{},"    define member: [user]\n",[57,635,636],{"class":59,"line":140},[57,637,399],{"emptyLinePlaceholder":398},[57,639,640],{"class":59,"line":146},[57,641,413],{},[57,643,644],{"class":59,"line":175},[57,645,418],{},[57,647,648],{"class":59,"line":193},[57,649,423],{},[57,651,652],{"class":59,"line":201},[57,653,428],{},[57,655,656],{"class":59,"line":207},[57,657,658],{},"    define viewer: [user, group#member] or editor\n",[10,660,661,662,665,666,668,669,672,673,675],{},"Le ",[19,663,664],{},"[user, group#member]"," dit que la relation ",[19,667,304],{}," accepte soit un utilisateur direct, ",[14,670,671],{},"soit"," le userset ",[19,674,343],{}," d'un groupe. Partageons maintenant avec toute l'equipe engineering en un tuple :",[48,677,679],{"className":464,"code":678,"language":466,"meta":53,"style":53},"[\n  { \"user\": \"group:engineering#member\", \"relation\": \"viewer\", \"object\": \"document:readme\" },\n  { \"user\": \"user:carol\", \"relation\": \"member\", \"object\": \"group:engineering\" }\n]\n",[19,680,681,685,738,791],{"__ignoreMap":53},[57,682,683],{"class":59,"line":60},[57,684,473],{"class":63},[57,686,687,689,691,693,695,697,699,702,704,706,708,710,712,714,716,718,720,722,724,726,728,730,732,734,736],{"class":59,"line":101},[57,688,478],{"class":63},[57,690,481],{"class":63},[57,692,75],{"class":94},[57,694,486],{"class":63},[57,696,489],{"class":63},[57,698,481],{"class":63},[57,700,701],{"class":494},"group:engineering#member",[57,703,486],{"class":63},[57,705,82],{"class":63},[57,707,481],{"class":63},[57,709,504],{"class":94},[57,711,486],{"class":63},[57,713,489],{"class":63},[57,715,481],{"class":63},[57,717,304],{"class":494},[57,719,486],{"class":63},[57,721,82],{"class":63},[57,723,481],{"class":63},[57,725,522],{"class":94},[57,727,486],{"class":63},[57,729,489],{"class":63},[57,731,481],{"class":63},[57,733,300],{"class":494},[57,735,486],{"class":63},[57,737,535],{"class":63},[57,739,740,742,744,746,748,750,752,755,757,759,761,763,765,767,769,771,773,775,777,779,781,783,785,787,789],{"class":59,"line":130},[57,741,478],{"class":63},[57,743,481],{"class":63},[57,745,75],{"class":94},[57,747,486],{"class":63},[57,749,489],{"class":63},[57,751,481],{"class":63},[57,753,754],{"class":494},"user:carol",[57,756,486],{"class":63},[57,758,82],{"class":63},[57,760,481],{"class":63},[57,762,504],{"class":94},[57,764,486],{"class":63},[57,766,489],{"class":63},[57,768,481],{"class":63},[57,770,343],{"class":494},[57,772,486],{"class":63},[57,774,82],{"class":63},[57,776,481],{"class":63},[57,778,522],{"class":94},[57,780,486],{"class":63},[57,782,489],{"class":63},[57,784,481],{"class":63},[57,786,347],{"class":494},[57,788,486],{"class":63},[57,790,590],{"class":63},[57,792,793],{"class":59,"line":140},[57,794,595],{"class":63},[10,796,797,798,800,801,804,805,807],{},"Carol peut maintenant voir le readme, et vous n'avez jamais touche au document pour l'accorder. Ajoutez quelqu'un a ",[19,799,701],{}," et il gagne l'acces a chaque document que ce groupe peut voir, instantanement et en un seul endroit. Les groupes peuvent contenir des groupes de la meme facon, en faisant accepter ",[19,802,803],{},"group#member"," au ",[19,806,343],{}," d'un groupe, et le controle parcourt simplement la chaine.",[40,809,811],{"id":810},"lheritage-le-dossier-qui-partage-son-acces-vers-le-bas","L'heritage : le dossier qui partage son acces vers le bas",[10,813,814,815,818,819,823,824,22],{},"Le dernier mouvement central est le ",[14,816,817],{},"tuple-to-userset",", le nom que Zanzibar donne a \"suivre une relation vers un autre objet et heriter de ",[820,821,822],"em",{},"sa"," relation\". C'est ainsi que les lecteurs d'un dossier deviennent les lecteurs de chaque document a l'interieur. OpenFGA l'ecrit ",[19,825,826],{},"X from Y",[48,828,830],{"className":379,"code":829,"language":381,"meta":53,"style":53},"type folder\n  relations\n    define viewer: [user, group#member]\n\ntype document\n  relations\n    define parent: [folder]\n    define owner: [user]\n    define editor: [user] or owner\n    define viewer: [user, group#member] or editor or viewer from parent\n",[19,831,832,837,841,846,850,854,858,863,867,871],{"__ignoreMap":53},[57,833,834],{"class":59,"line":60},[57,835,836],{},"type folder\n",[57,838,839],{"class":59,"line":101},[57,840,418],{},[57,842,843],{"class":59,"line":130},[57,844,845],{},"    define viewer: [user, group#member]\n",[57,847,848],{"class":59,"line":140},[57,849,399],{"emptyLinePlaceholder":398},[57,851,852],{"class":59,"line":146},[57,853,413],{},[57,855,856],{"class":59,"line":175},[57,857,418],{},[57,859,860],{"class":59,"line":193},[57,861,862],{},"    define parent: [folder]\n",[57,864,865],{"class":59,"line":201},[57,866,423],{},[57,868,869],{"class":59,"line":207},[57,870,428],{},[57,872,873],{"class":59,"line":212},[57,874,875],{},"    define viewer: [user, group#member] or editor or viewer from parent\n",[10,877,878,879,882,883,885,886,889],{},"La nouvelle clause ",[19,880,881],{},"viewer from parent"," signifie \"aussi quiconque est ",[19,884,304],{}," de l'objet dans ma relation ",[19,887,888],{},"parent","\". Reliez un document a un dossier avec un tuple et tout l'ensemble des lecteurs du dossier coule vers le bas :",[48,891,893],{"className":464,"code":892,"language":466,"meta":53,"style":53},"[\n  { \"user\": \"folder:handbook\", \"relation\": \"parent\",  \"object\": \"document:readme\" },\n  { \"user\": \"user:dave\",       \"relation\": \"viewer\",  \"object\": \"folder:handbook\" }\n]\n",[19,894,895,899,952,1006],{"__ignoreMap":53},[57,896,897],{"class":59,"line":60},[57,898,473],{"class":63},[57,900,901,903,905,907,909,911,913,916,918,920,922,924,926,928,930,932,934,936,938,940,942,944,946,948,950],{"class":59,"line":101},[57,902,478],{"class":63},[57,904,481],{"class":63},[57,906,75],{"class":94},[57,908,486],{"class":63},[57,910,489],{"class":63},[57,912,481],{"class":63},[57,914,915],{"class":494},"folder:handbook",[57,917,486],{"class":63},[57,919,82],{"class":63},[57,921,481],{"class":63},[57,923,504],{"class":94},[57,925,486],{"class":63},[57,927,489],{"class":63},[57,929,481],{"class":63},[57,931,888],{"class":494},[57,933,486],{"class":63},[57,935,82],{"class":63},[57,937,501],{"class":63},[57,939,522],{"class":94},[57,941,486],{"class":63},[57,943,489],{"class":63},[57,945,481],{"class":63},[57,947,300],{"class":494},[57,949,486],{"class":63},[57,951,535],{"class":63},[57,953,954,956,958,960,962,964,966,969,971,973,976,978,980,982,984,986,988,990,992,994,996,998,1000,1002,1004],{"class":59,"line":130},[57,955,478],{"class":63},[57,957,481],{"class":63},[57,959,75],{"class":94},[57,961,486],{"class":63},[57,963,489],{"class":63},[57,965,481],{"class":63},[57,967,968],{"class":494},"user:dave",[57,970,486],{"class":63},[57,972,82],{"class":63},[57,974,975],{"class":63},"       \"",[57,977,504],{"class":94},[57,979,486],{"class":63},[57,981,489],{"class":63},[57,983,481],{"class":63},[57,985,304],{"class":494},[57,987,486],{"class":63},[57,989,82],{"class":63},[57,991,501],{"class":63},[57,993,522],{"class":94},[57,995,486],{"class":63},[57,997,489],{"class":63},[57,999,481],{"class":63},[57,1001,915],{"class":494},[57,1003,486],{"class":63},[57,1005,590],{"class":63},[57,1007,1008],{"class":59,"line":140},[57,1009,595],{"class":63},[10,1011,1012,1013,1015,1016,1019,1020,1023],{},"Dave peut voir le readme parce qu'il peut voir son dossier parent. Imbriquez des dossiers dans des dossiers et l'acces se propage jusqu'en bas, evalue a la demande. Trois primitives, l'union (",[19,1014,453],{},"), l'intersection (",[19,1017,1018],{},"and",", pour \"doit etre les deux\"), et le tuple-to-userset (",[19,1021,1022],{},"from","), suffisent a exprimer quasiment tout schema de permission du monde reel. Cette economie est la raison pour laquelle le modele a tenu.",[40,1025,1027],{"id":1026},"la-vraie-partie-difficile-la-coherence-a-lechelle-globale","La vraie partie difficile : la coherence a l'echelle globale",[10,1029,1030,1031,22],{},"Tout ce qui precede est un modele de donnees propre. Il serait aussi inutile a l'echelle de Google sans resoudre le probleme que le modele a discretement cree : ce graphe est enorme, shardе a travers la planete, mis en cache partout, et lu des milliards de fois par jour. On ne peut pas re-parcourir le graphe complet a chaque controle. Donc on met en cache. Et des l'instant ou on met l'autorisation en cache, on herite du ",[14,1032,1033],{},"probleme du nouvel ennemi",[10,1035,1036],{},"Imaginez. Alice retire Bob d'un document, puis le modifie pour ajouter un secret. Deux ecritures, dans cet ordre, avec un lien causal : elle a revoque *parce qu'*elle allait ajouter le secret. Si une replique quelque part a quelques secondes de retard et pense encore que Bob est lecteur, Bob lit le secret. Chaque reponse individuelle etait \"correcte\" pour un instant passe, mais les servir dans le desordre viole l'ordre causal sur lequel l'utilisateur comptait. Les lectures perimees sont d'habitude une note de bas de page de performance. En autorisation, c'est une faille de securite.",[10,1038,1039,1040,1043,1044,1047,1048,1051],{},"Le remede de Zanzibar est un jeton d'instantane qu'il appelle ",[14,1041,1042],{},"zookie",". Quand l'ACL change, l'appelant recoit un zookie, un jeton opaque encodant un horodatage. Quand l'application demande plus tard \"Bob peut-il voir ceci ?\", elle passe le zookie correspondant au contenu qu'elle s'apprete a montrer. Zanzibar garantit alors que le controle est evalue sur un instantane ",[14,1045,1046],{},"au moins aussi frais"," que ce jeton. Il a le droit de lire depuis une replique bon marche et un peu perimee, mais jamais plus vieille que le contenu protege. La regle en une ligne : ",[14,1049,1050],{},"le controle de permission ne doit jamais etre plus perime que la donnee qu'il protege."," Les zookies rendent \"assez frais\" precis au lieu d'esperer que chaque replique soit a jour.",[10,1053,1054,1055,1058],{},"En dessous, cela s'appuie sur ",[14,1056,1057],{},"Spanner"," et son horloge TrueTime, qui donne a Zanzibar des horodatages globalement ordonnes et une coherence externe : une ecriture terminee avant une autre obtient vraiment un horodatage plus ancien, partout, sans verrou global sur le chemin de lecture. Les lectures d'instantane a un horodatage de peremption bornee sont ce qui permet a Zanzibar de servir la plupart des controles depuis des repliques locales en quelques millisecondes tout en refusant de servir une reponse perimee dangereuse. La coherence n'est pas gratuite, elle est achetee avec TrueTime, et cette dependance explique en grande partie pourquoi l'article se lit comme de la recherche en systemes distribues et non un tutoriel CRUD.",[10,1060,1061,1062,1065],{},"L'autre astuce d'echelle a connaitre par son nom est ",[14,1063,1064],{},"Leopard",", un index qui pre-aplatit l'appartenance a des groupes profondement imbriques. Certains groupes s'imbriquent sur des dizaines de niveaux avec des millions de membres, et parcourir cela en direct a chaque controle exploserait le budget de latence. Leopard maintient une representation d'ensemble denormalisee et mise a jour de facon incrementale, de sorte qu'une question \"X est-il membre de ce geant groupe imbrique ?\" devienne une recherche d'ensemble rapide plutot qu'un parcours de graphe profond. C'est le cache specialise qui garde le modele general rapide sur ses entrees les plus difficiles.",[40,1067,1069],{"id":1068},"les-chiffres-pour-que-la-complexite-ait-du-sens","Les chiffres, pour que la complexite ait du sens",[10,1071,1072,1073,1076,1077,1080,1081,1084,1085,1088,1089,1092],{},"Il est facile de lire tout cela et de penser que c'est sur-ingenie. L'echelle est la raison pour laquelle ca ne l'est pas. D'apres l'article : Zanzibar sert ",[14,1074,1075],{},"plus de 10 millions de controles d'autorisation par seconde",", en garde ",[14,1078,1079],{},"plus de 95 % sous 10 millisecondes"," (et 99 % sous une borne de quelques dizaines de ms), detient des ",[14,1082,1083],{},"milliers de milliards de tuples de relation"," sur ",[14,1086,1087],{},"plus de 1 500 serveurs"," dans des dizaines de clusters dans le monde, et tourne a ",[14,1090,1091],{},"plus de 99,999 % de disponibilite"," depuis des annees. Chacune des parties difficiles, zookies, Spanner, Leopard, existe pour tenir l'un de ces chiffres sans en casser un autre. La coherence sans la latence. La latence sans la securite perimee. L'echelle sans ni l'un ni l'autre.",[40,1094,1096],{"id":1095},"vous-navez-pas-besoin-de-google-pour-utiliser-ceci","Vous n'avez pas besoin de Google pour utiliser ceci",[10,1098,1099],{},"Vous ne ferez presque certainement jamais tourner Zanzibar. Vous n'en avez pas besoin. Le modele a survecu a l'article sous la forme d'une famille de moteurs open source que vous pouvez deployer cet apres-midi :",[229,1101,1102,1107,1117],{},[232,1103,1104,1106],{},[14,1105,365],{}," (CNCF, issu d'Auth0\u002FOkta), utilise dans tout cet article. DSL accessible, API HTTP et gRPC, SDK dans la plupart des langages.",[232,1108,1109,1112,1113,1116],{},[14,1110,1111],{},"SpiceDB"," (AuthZed). L'implementation la plus fidele a l'article, avec un vrai equivalent du zookie qu'elle appelle ",[14,1114,1115],{},"ZedToken"," et des modes de coherence reglables.",[232,1118,1119,1122],{},[14,1120,1121],{},"Ory Keto",", une implementation Go plus ancienne du modele de tuples.",[10,1124,1125,1126,1129,1130,1133,1134,1137,1138,1141],{},"Le bouton de coherence apparait directement dans ces outils. OpenFGA laisse une requete ",[19,1127,1128],{},"Check"," passer une preference ",[19,1131,1132],{},"consistency"," : ",[19,1135,1136],{},"MINIMIZE_LATENCY"," lit depuis le cache et est le defaut, tandis que ",[19,1139,1140],{},"HIGHER_CONSISTENCY"," force une evaluation fraiche, le meme compromis que les zookies encodent, expose comme un choix par appel. Le ZedToken de SpiceDB en est l'analogue le plus proche : vous stockez le jeton recu d'une ecriture et le passez aux controles ulterieurs pour exiger \"au moins aussi frais que ceci\".",[10,1143,1144,1145,1148],{},"Une facon pragmatique de l'adopter : gardez les donnees de votre app la ou elles sont, et deplacez seulement la ",[820,1146,1147],{},"decision d'autorisation"," dans le moteur ReBAC. A chaque ecriture qui change l'acces, poussez un tuple. A chaque controle, demandez au moteur. Vous obtenez la source unique de verite, le chemin d'audit et le modele d'heritage sans reecrire votre base de donnees.",[40,1150,1152],{"id":1151},"rbac-abac-rebac-quand-choisir-lequel","RBAC, ABAC, ReBAC : quand choisir lequel",[10,1154,1155],{},"Le ReBAC n'est pas automatiquement la reponse. Adaptez le modele a la forme de vos permissions.",[1157,1158,1159,1178],"table",{},[1160,1161,1162],"thead",{},[1163,1164,1165,1169,1172,1175],"tr",{},[1166,1167,1168],"th",{},"Modele",[1166,1170,1171],{},"Repond via",[1166,1173,1174],{},"Brille quand",[1166,1176,1177],{},"Peine quand",[1179,1180,1181,1199,1219],"tbody",{},[1163,1182,1183,1190,1193,1196],{},[1184,1185,1186,1189],"td",{},[14,1187,1188],{},"RBAC"," (roles)",[1184,1191,1192],{},"\"Quel role a l'utilisateur ?\"",[1184,1194,1195],{},"Petit jeu fixe de roles, ressources plates",[1184,1197,1198],{},"Partage par objet, heritage, \"qui peut voir X ?\"",[1163,1200,1201,1207,1210,1213],{},[1184,1202,1203,1206],{},[14,1204,1205],{},"ABAC"," (attributs)",[1184,1208,1209],{},"\"Les attributs utilisateur\u002Fressource satisfont-ils une politique ?\"",[1184,1211,1212],{},"Regles sur le contexte (heure, region, habilitation)",[1184,1214,1215,1216],{},"Relations et imbrication ; audit du ",[820,1217,1218],{},"pourquoi",[1163,1220,1221,1226,1229,1232],{},[1184,1222,1223,1225],{},[14,1224,319],{}," (relations)",[1184,1227,1228],{},"\"Y a-t-il un chemin de relations de l'utilisateur a l'objet ?\"",[1184,1230,1231],{},"Partage, groupes, dossiers, hierarchies, droits par objet",[1184,1233,1234],{},"Regles purement d'attributs sans relations",[10,1236,1237,1238,1241,1242,1244,1245,1248],{},"La plupart des vrais systemes sont un melange, et la bonne nouvelle est que les moteurs ReBAC laissent de plus en plus attacher des conditions (OpenFGA a des ",[14,1239,1240],{},"tuples conditionnels",", des controles contextuels facon ",[19,1243,1018],{},") pour replier un peu d'ABAC dans le graphe quand vous voulez \"editor ",[14,1246,1247],{},"et"," seulement pendant les heures ouvrees\".",[40,1250,1252],{"id":1251},"a-retenir","A retenir",[229,1254,1255,1264,1273,1287,1293],{},[232,1256,1257,1260,1261,1263],{},[14,1258,1259],{},"L'autorisation est un probleme de graphe deguise."," Des que vous avez du partage, des groupes ou de l'heritage, \"cet utilisateur peut-il faire ceci ?\" est une question d'accessibilite, et la modeliser en ",[19,1262,21],{}," disperses est la raison pour laquelle elle pourrit.",[232,1265,1266,1269,1270,1272],{},[14,1267,1268],{},"Un seul format de tuple exprime tout."," ",[19,1271,312],{},", avec les usersets et les regles de reecriture, effondre roles, groupes, imbrication et heritage dans un seul modele uniforme qu'un moteur peut raisonner et auditer.",[232,1274,1275,1269,1278,1280,1281,1283,1284,1286],{},[14,1276,1277],{},"Union, intersection et tuple-to-userset sont tout le langage.",[19,1279,453],{},", ",[19,1282,1018],{}," et ",[19,1285,1022],{}," dans OpenFGA couvrent quasiment tout schema de permission que vous rencontrerez.",[232,1288,1289,1292],{},[14,1290,1291],{},"Mettre l'autorisation en cache cree le probleme du nouvel ennemi, et les zookies le resolvent."," Le controle ne doit jamais etre plus perime que la donnee qu'il protege. Zanzibar achete cette garantie avec le TrueTime de Spanner ; le ZedToken de SpiceDB et les modes de coherence d'OpenFGA vous exposent le meme compromis.",[232,1294,1295,1298],{},[14,1296,1297],{},"Vous pouvez avoir le modele sans l'infrastructure."," OpenFGA, SpiceDB et Ory Keto mettent le design de Zanzibar a portee aujourd'hui. Deplacez la decision, pas vos donnees, et vous obtenez une source unique de verite et une vraie reponse a \"pourquoi Bob a-t-il l'acces ?\".",[10,1300,1301],{},"La lecon durable de Zanzibar n'est pas les dix millions de controles par seconde. C'est le recadrage : cessez de traiter la permission comme un booleen calcule a la hate, et commencez a la traiter comme une relation que vous stockez, pour que la reponse a chaque question d'acces soit un fait que vous pouvez consulter, expliquer et croire.",[1303,1304,1305],"style",{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}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 pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}",{"title":53,"searchDepth":101,"depth":101,"links":1307},[1308,1309,1310,1311,1312,1313,1314,1315,1316,1317],{"id":42,"depth":101,"text":43},{"id":278,"depth":101,"text":279},{"id":358,"depth":101,"text":359},{"id":610,"depth":101,"text":611},{"id":810,"depth":101,"text":811},{"id":1026,"depth":101,"text":1027},{"id":1068,"depth":101,"text":1069},{"id":1095,"depth":101,"text":1096},{"id":1151,"depth":101,"text":1152},{"id":1251,"depth":101,"text":1252},"2026-07-18","L'autorisation ressemble a un if d'une ligne, jusqu'a ce qu'on l'execute dix millions de fois par seconde sur tous les produits de Google. Zanzibar est le systeme qui a rendu cette question rapide, coherente et globale. On part du controle de permission naif pour arriver au controle d'acces base sur les relations, au modele de tuples, a la coherence avec les zookies, et aux heritiers open source comme OpenFGA que vous pouvez utiliser des aujourd'hui.",false,"md",null,{},"\u002Fsecurity\u002Fauthz\u002Fzanzibar-explained.fr",{"title":5,"description":1319},"zanzibar-demystifie-rebac-openfga","3.security\u002F2.authz\u002F1.zanzibar-explained.fr",[1329,319,1330,1331,365],"Authorization","Security","Distributed-Systems",[1333,1334,1335],"authz","backend","platform-engineering","oZc0ctzIgn_1aFMzO9rW___q8aEfRMOa6MgRSAg5jQM",{"id":1338,"title":1339,"body":1340,"date":1318,"description":2487,"draft":1320,"extension":1321,"img":1322,"meta":2488,"navigation":398,"path":2489,"seo":2490,"slug":2491,"stem":2492,"tags":2493,"topics":2494,"__hash__":2495},"content\u002F3.security\u002F2.authz\u002F1.zanzibar-explained.md","Zanzibar Demystified: How Google Answers 'Can This User Do This?'",{"type":7,"value":1341,"toc":2475},[1342,1351,1360,1366,1370,1373,1493,1496,1532,1539,1543,1550,1555,1570,1576,1586,1594,1609,1613,1624,1627,1671,1692,1695,1811,1814,1820,1823,1827,1830,1870,1885,2001,2013,2017,2029,2073,2085,2201,2213,2217,2223,2230,2244,2250,2256,2260,2283,2287,2290,2310,2325,2332,2336,2339,2409,2422,2426,2470,2473],[10,1343,1344,1345,1348,1349,22],{},"Every application ever written has to answer one question, over and over: ",[14,1346,1347],{},"can this user do this thing to that resource?"," Can Alice read this document. Can Bob delete this comment. Can this service call that endpoint. It feels like the most boring line of code in the building, a single ",[19,1350,21],{},[10,1352,1353,1354,1356,1357,1359],{},"Then the product grows. The document lives in a folder, and folder access should cascade. The document is shared with a team, and the team has sub-teams, and people join and leave. Someone in support needs to answer \"why can Bob see this file?\" and nobody can, because the answer is smeared across six services each with its own ",[19,1355,21],{},". Now a user is removed from a group and, for the next thirty seconds, a stale cache somewhere still says yes. That is not a boring ",[19,1358,21],{}," anymore. That is a distributed-systems problem wearing a boolean's clothing.",[10,1361,1362,1363,1365],{},"Google hit this at a scale nobody else had: one authorization question that Docs, Drive, Photos, Calendar, YouTube and Cloud all needed to ask, correctly, billions of times a day, in under ten milliseconds, everywhere on Earth. Their answer was ",[14,1364,37],{},", described in a 2019 paper that quietly became the most influential authorization design of the decade. This walks through what it actually is, why each hard part is hard, and how you can use the same model today through open-source heirs like OpenFGA without running Google's infrastructure.",[40,1367,1369],{"id":1368},"the-question-that-looks-simple-and-is-not","The question that looks simple and is not",[10,1371,1372],{},"Start with the version everyone writes first. Permissions live next to the resource, and you check them inline.",[48,1374,1375],{"className":50,"code":51,"language":52,"meta":53,"style":53},[19,1376,1377,1401,1421,1427,1431,1453,1469,1475,1479,1483,1489],{"__ignoreMap":53},[57,1378,1379,1381,1383,1385,1387,1389,1391,1393,1395,1397,1399],{"class":59,"line":60},[57,1380,64],{"class":63},[57,1382,68],{"class":67},[57,1384,71],{"class":63},[57,1386,75],{"class":74},[57,1388,79],{"class":78},[57,1390,82],{"class":63},[57,1392,85],{"class":74},[57,1394,88],{"class":78},[57,1396,91],{"class":63},[57,1398,95],{"class":94},[57,1400,98],{"class":63},[57,1402,1403,1405,1407,1409,1411,1413,1415,1417,1419],{"class":59,"line":101},[57,1404,105],{"class":104},[57,1406,85],{"class":108},[57,1408,22],{"class":63},[57,1410,113],{"class":108},[57,1412,116],{"class":63},[57,1414,119],{"class":108},[57,1416,22],{"class":63},[57,1418,124],{"class":108},[57,1420,127],{"class":63},[57,1422,1423,1425],{"class":59,"line":130},[57,1424,133],{"class":104},[57,1426,137],{"class":136},[57,1428,1429],{"class":59,"line":140},[57,1430,143],{"class":63},[57,1432,1433,1435,1437,1439,1441,1443,1445,1447,1449,1451],{"class":59,"line":146},[57,1434,149],{"class":104},[57,1436,152],{"class":108},[57,1438,82],{"class":63},[57,1440,157],{"class":108},[57,1442,160],{"class":63},[57,1444,163],{"class":104},[57,1446,85],{"class":108},[57,1448,22],{"class":63},[57,1450,170],{"class":108},[57,1452,127],{"class":63},[57,1454,1455,1457,1459,1461,1463,1465,1467],{"class":59,"line":175},[57,1456,178],{"class":104},[57,1458,157],{"class":108},[57,1460,116],{"class":63},[57,1462,119],{"class":108},[57,1464,22],{"class":63},[57,1466,124],{"class":108},[57,1468,127],{"class":63},[57,1470,1471,1473],{"class":59,"line":193},[57,1472,196],{"class":104},[57,1474,137],{"class":136},[57,1476,1477],{"class":59,"line":201},[57,1478,204],{"class":63},[57,1480,1481],{"class":59,"line":207},[57,1482,143],{"class":63},[57,1484,1485,1487],{"class":59,"line":212},[57,1486,215],{"class":104},[57,1488,218],{"class":136},[57,1490,1491],{"class":59,"line":221},[57,1492,224],{"class":63},[10,1494,1495],{},"On day one this is fine. The trouble is not that it is wrong, it is that it does not compose, and authorization is all about composition:",[229,1497,1498,1507,1516,1521,1526],{},[232,1499,1500,1503,1504,1506],{},[14,1501,1502],{},"Inheritance."," A document in a folder should inherit the folder's viewers. Now ",[19,1505,240],{}," needs to know about folders. Folders can nest, so it needs recursion.",[232,1508,1509,1512,1513,1515],{},[14,1510,1511],{},"Groups."," Share with a team, not a list of user IDs. Teams contain teams. Now ",[19,1514,240],{}," walks a group graph.",[232,1517,1518,1520],{},[14,1519,255],{}," Docs, Drive and Photos each grow their own copy of this logic, and they drift. A permission bug in one is not a bug in the others, which is worse, because you cannot reason about the system as a whole.",[232,1522,1523,1525],{},[14,1524,261],{}," \"Why does Bob have access?\" has no answer, because access is a computation scattered across services and rows, not a fact you can look up.",[232,1527,1528,1531],{},[14,1529,1530],{},"Consistency."," You revoke Bob and immediately change what he was looking at. If a cache serves the old answer for even a moment, Bob sees content he should not. The paper calls this the \"new enemy\" problem, and it is the whole reason Zanzibar is complicated.",[10,1533,1534,1535,1538],{},"RBAC (roles) and ABAC (attributes) each solve part of this and neither solves the shape of it, because the real shape is a ",[14,1536,1537],{},"graph",": users relate to groups, groups to folders, folders to documents, and a permission check is a question about reachability in that graph. That reframe is the entire idea behind Zanzibar.",[40,1540,1542],{"id":1541},"the-core-idea-permissions-are-relationships","The core idea: permissions are relationships",[10,1544,1545,1546,1549],{},"Zanzibar stores authorization as a single, uniform kind of fact called a ",[14,1547,1548],{},"relation tuple",". Read one out loud and it is an English sentence:",[48,1551,1553],{"className":1552,"code":291,"language":292},[290],[19,1554,291],{"__ignoreMap":53},[10,1556,1557,1558,1560,1561,1563,1564,1566,1567,1569],{},"\"On object ",[19,1559,300],{},", the ",[19,1562,304],{}," relation includes ",[19,1565,308],{},".\" Alice can view the readme. Every permission in the entire company reduces to a pile of these tuples of the form ",[19,1568,312],{},". There is no per-product schema of columns and join tables; there is one table of relationships, and a permission check is a graph query over it.",[10,1571,1572,1573,1575],{},"This is ",[14,1574,319],{},", relationship-based access control. The insight is that \"role\" and \"attribute\" are both just special cases of \"relationship.\" Owner is a relationship. Membership is a relationship. Parent folder is a relationship. Once everything is the same kind of edge, one engine can answer every question, and the audit story falls out for free: \"why does Bob have access?\" becomes \"show me the path of tuples from Bob to this document,\" which is a query, not an archaeology dig.",[10,1577,1578,1579,1581,1582,1585],{},"The subject on the right of the ",[19,1580,326],{}," can itself be a ",[14,1583,1584],{},"set"," of users, not just one, and this is where the model gets its power. Instead of naming Alice, point at everyone who is a member of a group:",[48,1587,1588],{"className":379,"code":335,"language":381,"meta":53,"style":53},[19,1589,1590],{"__ignoreMap":53},[57,1591,1592],{"class":59,"line":60},[57,1593,335],{},[10,1595,1596,1597,1599,1600,1602,1603,1605,1606,1608],{},"\"Everyone in the ",[19,1598,343],{}," relation of ",[19,1601,347],{}," is a viewer of the readme.\" This is a ",[14,1604,351],{},": a reference to \"the members of this relation on this object.\" Usersets are how groups, nesting and inheritance all collapse into the same tuple format. A group with sub-groups is just a tuple whose subject is another group's ",[19,1607,343],{}," userset. The graph builds itself.",[40,1610,1612],{"id":1611},"modeling-it-with-openfga","Modeling it with OpenFGA",[10,1614,1615,1616,1618,1619,1621,1622,22],{},"Zanzibar itself is internal to Google, but its model is now an open standard you can run. ",[14,1617,365],{}," (a CNCF project, born at Auth0\u002FOkta) is the most widely used implementation, and its DSL makes the model concrete. A model has ",[14,1620,369],{}," (the kinds of objects), and each type declares ",[14,1623,373],{},[10,1625,1626],{},"Start with the simplest useful model: documents with three roles, where each role implies the weaker one.",[48,1628,1629],{"className":379,"code":380,"language":381,"meta":53,"style":53},[19,1630,1631,1635,1639,1643,1647,1651,1655,1659,1663,1667],{"__ignoreMap":53},[57,1632,1633],{"class":59,"line":60},[57,1634,388],{},[57,1636,1637],{"class":59,"line":101},[57,1638,393],{},[57,1640,1641],{"class":59,"line":130},[57,1642,399],{"emptyLinePlaceholder":398},[57,1644,1645],{"class":59,"line":140},[57,1646,404],{},[57,1648,1649],{"class":59,"line":146},[57,1650,399],{"emptyLinePlaceholder":398},[57,1652,1653],{"class":59,"line":175},[57,1654,413],{},[57,1656,1657],{"class":59,"line":193},[57,1658,418],{},[57,1660,1661],{"class":59,"line":201},[57,1662,423],{},[57,1664,1665],{"class":59,"line":207},[57,1666,428],{},[57,1668,1669],{"class":59,"line":212},[57,1670,433],{},[10,1672,1673,1674,1676,1677,1679,1680,1682,1683,1685,1686,1688,1689,1691],{},"Read the rewrite rules on the right. ",[19,1675,439],{}," is \"anyone directly assigned editor, ",[14,1678,453],{}," anyone who is owner.\" ",[19,1681,304],{}," is \"anyone directly assigned viewer, ",[14,1684,453],{}," anyone who is editor.\" So owners are editors are viewers, for free, with no duplicated tuples. That ",[19,1687,453],{}," is a set ",[14,1690,457],{},", and it is the same union Zanzibar's config calls a userset rewrite. The engine computes it at check time; you never store the derived tuples.",[10,1693,1694],{},"Now the actual permission facts, the tuples:",[48,1696,1697],{"className":464,"code":465,"language":466,"meta":53,"style":53},[19,1698,1699,1703,1755,1807],{"__ignoreMap":53},[57,1700,1701],{"class":59,"line":60},[57,1702,473],{"class":63},[57,1704,1705,1707,1709,1711,1713,1715,1717,1719,1721,1723,1725,1727,1729,1731,1733,1735,1737,1739,1741,1743,1745,1747,1749,1751,1753],{"class":59,"line":101},[57,1706,478],{"class":63},[57,1708,481],{"class":63},[57,1710,75],{"class":94},[57,1712,486],{"class":63},[57,1714,489],{"class":63},[57,1716,481],{"class":63},[57,1718,308],{"class":494},[57,1720,486],{"class":63},[57,1722,82],{"class":63},[57,1724,501],{"class":63},[57,1726,504],{"class":94},[57,1728,486],{"class":63},[57,1730,489],{"class":63},[57,1732,481],{"class":63},[57,1734,513],{"class":494},[57,1736,486],{"class":63},[57,1738,82],{"class":63},[57,1740,501],{"class":63},[57,1742,522],{"class":94},[57,1744,486],{"class":63},[57,1746,489],{"class":63},[57,1748,481],{"class":63},[57,1750,300],{"class":494},[57,1752,486],{"class":63},[57,1754,535],{"class":63},[57,1756,1757,1759,1761,1763,1765,1767,1769,1771,1773,1775,1777,1779,1781,1783,1785,1787,1789,1791,1793,1795,1797,1799,1801,1803,1805],{"class":59,"line":130},[57,1758,478],{"class":63},[57,1760,481],{"class":63},[57,1762,75],{"class":94},[57,1764,486],{"class":63},[57,1766,489],{"class":63},[57,1768,481],{"class":63},[57,1770,552],{"class":494},[57,1772,486],{"class":63},[57,1774,82],{"class":63},[57,1776,559],{"class":63},[57,1778,504],{"class":94},[57,1780,486],{"class":63},[57,1782,489],{"class":63},[57,1784,481],{"class":63},[57,1786,304],{"class":494},[57,1788,486],{"class":63},[57,1790,82],{"class":63},[57,1792,481],{"class":63},[57,1794,522],{"class":94},[57,1796,486],{"class":63},[57,1798,489],{"class":63},[57,1800,481],{"class":63},[57,1802,300],{"class":494},[57,1804,486],{"class":63},[57,1806,590],{"class":63},[57,1808,1809],{"class":59,"line":140},[57,1810,595],{"class":63},[10,1812,1813],{},"Alice owns it, Bob can view it. A check reads:",[48,1815,1818],{"className":1816,"code":1817,"language":292},[290],"check(user:alice, viewer, document:readme)  →  allowed: true   \u002F\u002F owner ⇒ editor ⇒ viewer\ncheck(user:bob,  editor, document:readme)  →  allowed: false  \u002F\u002F only a direct viewer\n",[19,1819,1817],{"__ignoreMap":53},[10,1821,1822],{},"Alice is a viewer even though no tuple says so, because the model derives it. That is the whole trick, made small.",[40,1824,1826],{"id":1825},"groups-without-ever-touching-the-document-again","Groups, without ever touching the document again",[10,1828,1829],{},"Add a group type and let a document be shared with a group's members. This is the userset from earlier, in OpenFGA syntax.",[48,1831,1832],{"className":379,"code":617,"language":381,"meta":53,"style":53},[19,1833,1834,1838,1842,1846,1850,1854,1858,1862,1866],{"__ignoreMap":53},[57,1835,1836],{"class":59,"line":60},[57,1837,624],{},[57,1839,1840],{"class":59,"line":101},[57,1841,418],{},[57,1843,1844],{"class":59,"line":130},[57,1845,633],{},[57,1847,1848],{"class":59,"line":140},[57,1849,399],{"emptyLinePlaceholder":398},[57,1851,1852],{"class":59,"line":146},[57,1853,413],{},[57,1855,1856],{"class":59,"line":175},[57,1857,418],{},[57,1859,1860],{"class":59,"line":193},[57,1861,423],{},[57,1863,1864],{"class":59,"line":201},[57,1865,428],{},[57,1867,1868],{"class":59,"line":207},[57,1869,658],{},[10,1871,1872,1873,1875,1876,1878,1879,1881,1882,1884],{},"The ",[19,1874,664],{}," says the ",[19,1877,304],{}," relation accepts either a direct user ",[14,1880,453],{}," the ",[19,1883,343],{}," userset of a group. Now share with the whole engineering team in one tuple:",[48,1886,1887],{"className":464,"code":678,"language":466,"meta":53,"style":53},[19,1888,1889,1893,1945,1997],{"__ignoreMap":53},[57,1890,1891],{"class":59,"line":60},[57,1892,473],{"class":63},[57,1894,1895,1897,1899,1901,1903,1905,1907,1909,1911,1913,1915,1917,1919,1921,1923,1925,1927,1929,1931,1933,1935,1937,1939,1941,1943],{"class":59,"line":101},[57,1896,478],{"class":63},[57,1898,481],{"class":63},[57,1900,75],{"class":94},[57,1902,486],{"class":63},[57,1904,489],{"class":63},[57,1906,481],{"class":63},[57,1908,701],{"class":494},[57,1910,486],{"class":63},[57,1912,82],{"class":63},[57,1914,481],{"class":63},[57,1916,504],{"class":94},[57,1918,486],{"class":63},[57,1920,489],{"class":63},[57,1922,481],{"class":63},[57,1924,304],{"class":494},[57,1926,486],{"class":63},[57,1928,82],{"class":63},[57,1930,481],{"class":63},[57,1932,522],{"class":94},[57,1934,486],{"class":63},[57,1936,489],{"class":63},[57,1938,481],{"class":63},[57,1940,300],{"class":494},[57,1942,486],{"class":63},[57,1944,535],{"class":63},[57,1946,1947,1949,1951,1953,1955,1957,1959,1961,1963,1965,1967,1969,1971,1973,1975,1977,1979,1981,1983,1985,1987,1989,1991,1993,1995],{"class":59,"line":130},[57,1948,478],{"class":63},[57,1950,481],{"class":63},[57,1952,75],{"class":94},[57,1954,486],{"class":63},[57,1956,489],{"class":63},[57,1958,481],{"class":63},[57,1960,754],{"class":494},[57,1962,486],{"class":63},[57,1964,82],{"class":63},[57,1966,481],{"class":63},[57,1968,504],{"class":94},[57,1970,486],{"class":63},[57,1972,489],{"class":63},[57,1974,481],{"class":63},[57,1976,343],{"class":494},[57,1978,486],{"class":63},[57,1980,82],{"class":63},[57,1982,481],{"class":63},[57,1984,522],{"class":94},[57,1986,486],{"class":63},[57,1988,489],{"class":63},[57,1990,481],{"class":63},[57,1992,347],{"class":494},[57,1994,486],{"class":63},[57,1996,590],{"class":63},[57,1998,1999],{"class":59,"line":140},[57,2000,595],{"class":63},[10,2002,2003,2004,2006,2007,2009,2010,2012],{},"Carol can now view the readme, and you never touched the document to grant it. Add someone to ",[19,2005,701],{}," and they gain access to every document that group can see, instantly and in one place. Groups can contain groups the same way, by making a group's ",[19,2008,343],{}," accept ",[19,2011,803],{},", and the check just walks the chain.",[40,2014,2016],{"id":2015},"inheritance-the-folder-that-shares-its-access-down","Inheritance: the folder that shares its access down",[10,2018,2019,2020,2022,2023,2026,2027,22],{},"The last core move is ",[14,2021,817],{},", Zanzibar's name for \"follow a relation to another object and inherit ",[820,2024,2025],{},"its"," relation.\" It is how a folder's viewers become the viewers of every document inside it. OpenFGA spells it ",[19,2028,826],{},[48,2030,2031],{"className":379,"code":829,"language":381,"meta":53,"style":53},[19,2032,2033,2037,2041,2045,2049,2053,2057,2061,2065,2069],{"__ignoreMap":53},[57,2034,2035],{"class":59,"line":60},[57,2036,836],{},[57,2038,2039],{"class":59,"line":101},[57,2040,418],{},[57,2042,2043],{"class":59,"line":130},[57,2044,845],{},[57,2046,2047],{"class":59,"line":140},[57,2048,399],{"emptyLinePlaceholder":398},[57,2050,2051],{"class":59,"line":146},[57,2052,413],{},[57,2054,2055],{"class":59,"line":175},[57,2056,418],{},[57,2058,2059],{"class":59,"line":193},[57,2060,862],{},[57,2062,2063],{"class":59,"line":201},[57,2064,423],{},[57,2066,2067],{"class":59,"line":207},[57,2068,428],{},[57,2070,2071],{"class":59,"line":212},[57,2072,875],{},[10,2074,2075,2076,2078,2079,2081,2082,2084],{},"The new clause ",[19,2077,881],{}," means \"also anyone who is a ",[19,2080,304],{}," of the object in my ",[19,2083,888],{}," relation.\" Wire a document to a folder with one tuple and the folder's entire viewer set flows down:",[48,2086,2087],{"className":464,"code":892,"language":466,"meta":53,"style":53},[19,2088,2089,2093,2145,2197],{"__ignoreMap":53},[57,2090,2091],{"class":59,"line":60},[57,2092,473],{"class":63},[57,2094,2095,2097,2099,2101,2103,2105,2107,2109,2111,2113,2115,2117,2119,2121,2123,2125,2127,2129,2131,2133,2135,2137,2139,2141,2143],{"class":59,"line":101},[57,2096,478],{"class":63},[57,2098,481],{"class":63},[57,2100,75],{"class":94},[57,2102,486],{"class":63},[57,2104,489],{"class":63},[57,2106,481],{"class":63},[57,2108,915],{"class":494},[57,2110,486],{"class":63},[57,2112,82],{"class":63},[57,2114,481],{"class":63},[57,2116,504],{"class":94},[57,2118,486],{"class":63},[57,2120,489],{"class":63},[57,2122,481],{"class":63},[57,2124,888],{"class":494},[57,2126,486],{"class":63},[57,2128,82],{"class":63},[57,2130,501],{"class":63},[57,2132,522],{"class":94},[57,2134,486],{"class":63},[57,2136,489],{"class":63},[57,2138,481],{"class":63},[57,2140,300],{"class":494},[57,2142,486],{"class":63},[57,2144,535],{"class":63},[57,2146,2147,2149,2151,2153,2155,2157,2159,2161,2163,2165,2167,2169,2171,2173,2175,2177,2179,2181,2183,2185,2187,2189,2191,2193,2195],{"class":59,"line":130},[57,2148,478],{"class":63},[57,2150,481],{"class":63},[57,2152,75],{"class":94},[57,2154,486],{"class":63},[57,2156,489],{"class":63},[57,2158,481],{"class":63},[57,2160,968],{"class":494},[57,2162,486],{"class":63},[57,2164,82],{"class":63},[57,2166,975],{"class":63},[57,2168,504],{"class":94},[57,2170,486],{"class":63},[57,2172,489],{"class":63},[57,2174,481],{"class":63},[57,2176,304],{"class":494},[57,2178,486],{"class":63},[57,2180,82],{"class":63},[57,2182,501],{"class":63},[57,2184,522],{"class":94},[57,2186,486],{"class":63},[57,2188,489],{"class":63},[57,2190,481],{"class":63},[57,2192,915],{"class":494},[57,2194,486],{"class":63},[57,2196,590],{"class":63},[57,2198,2199],{"class":59,"line":140},[57,2200,595],{"class":63},[10,2202,2203,2204,2206,2207,2209,2210,2212],{},"Dave can view the readme because he can view its parent folder. Nest folders inside folders and access cascades the whole way down, evaluated on demand. Three primitives, union (",[19,2205,453],{},"), intersection (",[19,2208,1018],{},", for \"must be both\"), and tuple-to-userset (",[19,2211,1022],{},"), are enough to express essentially every real-world permission scheme. That economy is why the model has held up.",[40,2214,2216],{"id":2215},"the-genuinely-hard-part-consistency-at-global-scale","The genuinely hard part: consistency at global scale",[10,2218,2219,2220,22],{},"Everything so far is a clean data model. It would also be useless at Google's scale without solving the problem the model quietly created: this graph is enormous, sharded across the planet, cached everywhere, and read billions of times a day. You cannot re-walk the full graph on every check. So you cache. And the moment you cache authorization, you inherit the ",[14,2221,2222],{},"new enemy problem",[10,2224,2225,2226,2229],{},"Picture it. Alice removes Bob from a document, then edits it to add a secret. Two writes, in that order, with a causal link between them: she revoked ",[820,2227,2228],{},"because"," she was about to add the secret. If a replica somewhere is a few seconds stale and still thinks Bob is a viewer, Bob reads the secret. The individual answers were each \"correct\" for some past instant, but serving them out of order violated the causal ordering the user relied on. Stale reads are usually a performance footnote. In authorization they are a security breach.",[10,2231,2232,2233,2235,2236,2239,2240,2243],{},"Zanzibar's fix is a snapshot token it calls a ",[14,2234,1042],{},". When the ACL changes, the caller gets back a zookie, an opaque token encoding a timestamp. When the application later asks \"can Bob view this?\", it passes the zookie that corresponds to the content it is about to show. Zanzibar then guarantees the check is evaluated at a snapshot ",[14,2237,2238],{},"at least as fresh"," as that token. It is allowed to read from a cheap, slightly stale replica, but never one older than the content being protected. The rule in one line: ",[14,2241,2242],{},"the permission check must never be staler than the data it guards."," Zookies make \"fresh enough\" precise instead of hoping every replica is current.",[10,2245,2246,2247,2249],{},"Underneath, this leans on ",[14,2248,1057],{}," and its TrueTime clock, which gives Zanzibar globally-ordered timestamps and external consistency: a write that finished before another really does get an earlier timestamp, everywhere, without a global lock on the read path. Snapshot reads at a bounded-staleness timestamp are what let Zanzibar serve most checks from local replicas in single-digit milliseconds while still refusing to serve a dangerous stale answer. The consistency is not free, it is bought with TrueTime, and that dependency is a big part of why the paper reads like distributed-systems research and not a CRUD tutorial.",[10,2251,2252,2253,2255],{},"The other scaling trick worth knowing by name is ",[14,2254,1064],{},", an index that pre-flattens deeply nested group membership. Some groups nest dozens of levels deep with millions of members, and walking that live on every check would blow the latency budget. Leopard maintains a denormalized, incrementally-updated set representation so a \"is X a member of this giant nested group?\" question becomes a fast set lookup instead of a deep graph traversal. It is the specialized cache that keeps the general model fast on its worst-shaped inputs.",[40,2257,2259],{"id":2258},"the-numbers-so-the-complexity-makes-sense","The numbers, so the complexity makes sense",[10,2261,2262,2263,2266,2267,2270,2271,2274,2275,2278,2279,2282],{},"It is easy to read all this and think it is over-engineered. The scale is why it is not. From the paper: Zanzibar serves ",[14,2264,2265],{},"more than 10 million authorization checks per second",", keeps ",[14,2268,2269],{},"over 95% of them under 10 milliseconds"," (and 99% under a small tens-of-ms bound), holds ",[14,2272,2273],{},"trillions of relation tuples"," across ",[14,2276,2277],{},"more than 1,500 servers"," in dozens of clusters worldwide, and has run at ",[14,2280,2281],{},"greater than 99.999% availability"," for years. Every one of the hard parts, zookies, Spanner, Leopard, exists to hold one of those numbers while not breaking another. Consistency without the latency. Latency without stale security. Scale without either.",[40,2284,2286],{"id":2285},"you-do-not-need-google-to-use-this","You do not need Google to use this",[10,2288,2289],{},"You will almost certainly never run Zanzibar. You do not need to. The model outlived the paper as a family of open-source engines you can deploy this afternoon:",[229,2291,2292,2297,2305],{},[232,2293,2294,2296],{},[14,2295,365],{}," (CNCF, from Auth0\u002FOkta), used throughout this article. Friendly DSL, HTTP and gRPC APIs, SDKs in most languages.",[232,2298,2299,2301,2302,2304],{},[14,2300,1111],{}," (AuthZed). The most faithful-to-the-paper implementation, with a real zookie equivalent it calls a ",[14,2303,1115],{}," and tunable consistency modes.",[232,2306,2307,2309],{},[14,2308,1121],{},", an earlier Go implementation of the tuple model.",[10,2311,2312,2313,2315,2316,2318,2319,2321,2322,2324],{},"The consistency knob shows up in these tools directly. OpenFGA lets a ",[19,2314,1128],{}," request pass a ",[19,2317,1132],{}," preference: ",[19,2320,1136],{}," reads from cache and is the default, while ",[19,2323,1140],{}," forces a fresh evaluation, the same trade-off zookies encode, exposed as a per-call choice. SpiceDB's ZedToken is the closer analog: you store the token you get from a write and pass it on later checks to demand \"at least this fresh.\"",[10,2326,2327,2328,2331],{},"A pragmatic way to adopt it: keep your app's data where it is, and move only the ",[820,2329,2330],{},"authorization decision"," into the ReBAC engine. On each write that changes access, push a tuple. On each check, ask the engine. You get the single source of truth, the audit path, and the inheritance model without rewriting your database.",[40,2333,2335],{"id":2334},"rbac-abac-rebac-when-to-reach-for-which","RBAC, ABAC, ReBAC: when to reach for which",[10,2337,2338],{},"ReBAC is not automatically the answer. Match the model to the shape of your permissions.",[1157,2340,2341,2357],{},[1160,2342,2343],{},[1163,2344,2345,2348,2351,2354],{},[1166,2346,2347],{},"Model",[1166,2349,2350],{},"Answers with",[1166,2352,2353],{},"Shines when",[1166,2355,2356],{},"Struggles when",[1179,2358,2359,2374,2393],{},[1163,2360,2361,2365,2368,2371],{},[1184,2362,2363,1189],{},[14,2364,1188],{},[1184,2366,2367],{},"\"What role does the user have?\"",[1184,2369,2370],{},"Small fixed set of roles, flat resources",[1184,2372,2373],{},"Per-object sharing, inheritance, \"who can see X?\"",[1163,2375,2376,2381,2384,2387],{},[1184,2377,2378,2380],{},[14,2379,1205],{}," (attributes)",[1184,2382,2383],{},"\"Do the user\u002Fresource attributes satisfy a policy?\"",[1184,2385,2386],{},"Rules over context (time, region, clearance)",[1184,2388,2389,2390],{},"Relationships and nesting; audit of ",[820,2391,2392],{},"why",[1163,2394,2395,2400,2403,2406],{},[1184,2396,2397,2399],{},[14,2398,319],{}," (relationships)",[1184,2401,2402],{},"\"Is there a path of relations from user to object?\"",[1184,2404,2405],{},"Sharing, groups, folders, hierarchies, per-object grants",[1184,2407,2408],{},"Purely attribute rules with no relationships",[10,2410,2411,2412,2415,2416,2418,2419,2421],{},"Most real systems are a blend, and the good news is ReBAC engines increasingly let you attach conditions (OpenFGA has ",[14,2413,2414],{},"conditional tuples",", contextual ",[19,2417,1018],{},"-style checks) so you can fold a bit of ABAC into the graph when you need \"editor ",[14,2420,1018],{}," only during business hours.\"",[40,2423,2425],{"id":2424},"takeaways","Takeaways",[229,2427,2428,2437,2445,2458,2464],{},[232,2429,2430,2433,2434,2436],{},[14,2431,2432],{},"Authorization is a graph problem in disguise."," The moment you have sharing, groups or inheritance, \"can this user do this?\" is a reachability question, and modeling it as scattered ",[19,2435,21],{}," statements is why it rots.",[232,2438,2439,1269,2442,2444],{},[14,2440,2441],{},"One tuple format expresses everything.",[19,2443,312],{},", with usersets and rewrite rules, collapses roles, groups, nesting and inheritance into a single uniform model an engine can reason about and audit.",[232,2446,2447,1269,2450,1280,2452,2454,2455,2457],{},[14,2448,2449],{},"Union, intersection and tuple-to-userset are the whole language.",[19,2451,453],{},[19,2453,1018],{},", and ",[19,2456,1022],{}," in OpenFGA cover essentially every permission scheme you will meet.",[232,2459,2460,2463],{},[14,2461,2462],{},"Caching authorization creates the new enemy problem, and zookies solve it."," The check must never be staler than the data it guards. Zanzibar buys that guarantee with Spanner's TrueTime; SpiceDB's ZedToken and OpenFGA's consistency modes expose the same trade-off to you.",[232,2465,2466,2469],{},[14,2467,2468],{},"You can have the model without the infrastructure."," OpenFGA, SpiceDB and Ory Keto put Zanzibar's design in reach today. Move the decision, not your data, and you get a single source of truth and a real answer to \"why does Bob have access?\"",[10,2471,2472],{},"Zanzibar's lasting lesson is not the ten million checks a second. It is the reframe: stop treating permission as a boolean you compute in a hurry, and start treating it as a relationship you store, so that the answer to every access question is a fact you can look up, explain and trust.",[1303,2474,1305],{},{"title":53,"searchDepth":101,"depth":101,"links":2476},[2477,2478,2479,2480,2481,2482,2483,2484,2485,2486],{"id":1368,"depth":101,"text":1369},{"id":1541,"depth":101,"text":1542},{"id":1611,"depth":101,"text":1612},{"id":1825,"depth":101,"text":1826},{"id":2015,"depth":101,"text":2016},{"id":2215,"depth":101,"text":2216},{"id":2258,"depth":101,"text":2259},{"id":2285,"depth":101,"text":2286},{"id":2334,"depth":101,"text":2335},{"id":2424,"depth":101,"text":2425},"Authorization looks like a one-line if statement until you run it ten million times a second across every product Google ships. Zanzibar is the system that made that question fast, consistent and global. This walks from the naive permission check to relationship-based access control, the tuple model, consistency with zookies, and the open-source heirs like OpenFGA you can actually run today.",{},"\u002Fsecurity\u002Fauthz\u002Fzanzibar-explained",{"title":1339,"description":2487},"zanzibar-demystified-rebac-openfga","3.security\u002F2.authz\u002F1.zanzibar-explained",[1329,319,1330,1331,365],[1333,1334,1335],"ROfbZVdRsIIboVABkqsAfo4csNFs7qtUUEpEM3mwtw8",[2497,2500],{"title":2498,"path":2499},"Comment un entretien m'a poussé à créer Obscura, un générateur de mots de passe avec une vraie entropie","\u002Fsecurity\u002Fintroducing-obscura.fr",{"title":2501,"path":2502},"De RBAC a ReBAC : migrer un systeme de roles vers OpenFGA sans coupure","\u002Fsecurity\u002Fauthz\u002Frbac-to-rebac.fr",[2504,2510,2521],{"path":2502,"title":2501,"description":2505,"date":2506,"tags":2507,"topics":2509},"Les roles marchent, jusqu'au jour ou quelqu'un dit 'partage juste ce document avec juste cette personne'. Ce jour-la, RBAC atteint ses limites. Voici la migration pratique : mapper les tables de roles vers des tuples de relation, faire tourner OpenFGA en shadow a cote de vos controles SQL, backfiller sans risque, puis seulement debloquer le partage par objet et la hierarchie que les roles n'ont jamais pu offrir. Avec les pieges dont personne ne parle.","2026-07-19",[1329,319,1188,365,2508],"Migration",[1333,1334,1335],{"path":2511,"title":2512,"description":2513,"date":2514,"tags":2515,"topics":2519},"\u002Fbackend\u002Fload-balancing\u002Fload-balancing-algorithms.fr","Algorithmes de load balancing : chacun repare le defaut du precedent","Repartir des requetes sur N serveurs sonne comme un one-liner : choisir un serveur, envoyer la requete. Puis un backend est plus lent, ou plus gros, ou tient une session, et le choix naif s'effondre. On parcourt les algorithmes classiques comme une chaine ou chacun existe pour reparer l'angle mort du precedent : round robin, pondere, least connections, power of two choices, et consistent hashing, en Go, jusqu'a la panne que tous les tutos oublient.","2026-07-20",[2516,2517,1331,2518],"Load-Balancing","Go","Resilience",[1334,2520,1335],"resilience",{"path":2522,"title":2523,"description":2524,"date":2525,"tags":2526,"topics":2528},"\u002Fbackend\u002Fapi-design\u002Fretry-storms.fr","Retry Storms : comment de bons clients font tomber des serveurs en bonne santé","Un retry semble inoffensif : la requête a échoué, on réessaie. Multipliez ça par chaque client, ajoutez une dépendance lente, et les retries deviennent un DDoS auto-infligé. On part de la boucle de retry naïve vers le backoff exponentiel, le jitter, les retry budgets et les circuit breakers, la moitié côté client de la résilience, qui complète le rate limiting côté serveur.","2026-07-11",[2518,1331,2517,2527],"Retries",[1334,1335,2520],1784570361759]