O paper que reordenou o campo
"Zanzibar: Google's Consistent, Global Authorization System" — Pang, Hoffmann, Wesołowski et al, USENIX ATC 2019. É o paper de autorização mais influente da década. Resolveu, em produção real, três problemas que ninguém mais tinha resolvido junto:
Leitura obrigatória: research.google/pubs/pub48190. Após o paper, surgiram SpiceDB (2021), OpenFGA (2022, Auth0/CNCF), e o próprio AWS Verified Permissions (Cedar, 2023) absorveu ideias. ReBAC virou mainstream — antes era hipotético.
O insight: relação como dado, não como policy
Em RBAC/ABAC clássicos, autorização é regra: "admins podem editar invoices". Em Zanzibar, é fato: doc:readme#editor@user:tom — isto é uma tupla, gravada no banco, mutável como qualquer linha. A engine apenas navega o grafo dessas tuplas.
# Exemplos de tuplas em produção
doc:readme#owner@user:tom
doc:readme#editor@user:alice
doc:readme#parent@folder:planning
folder:planning#viewer@group:eng#member
group:eng#member@user:bob
# Pergunta: Bob pode ler doc:readme?
# Resposta: SIM. doc:readme tem parent folder:planning;
# folder:planning tem viewer = group:eng#member;
# user:bob é member do group:eng. ✓Userset rewrites: o engine de derivação
O modelo "tupla é fato" não basta — você precisa derivar "viewer" a partir de "editor" (todo editor é viewer), "viewer" a partir de "folder.viewer" (herança), etc. Isso é o userset rewrite — descrito no schema do namespace.
# Schema do namespace 'doc' (sintaxe simplificada do paper)
name: "doc"
relation { text: "owner" }
relation { text: "editor"
userset_rewrite {
union {
child { _this {} } # tuplas diretas
child { computed_userset { relation: "owner" } } # owner ⊆ editor
}
}
}
relation { text: "viewer"
userset_rewrite {
union {
child { _this {} }
child { computed_userset { relation: "editor" } }
child { tuple_to_userset { # herança do folder pai
tupleset { relation: "parent" }
computed_userset { object: "$TUPLE_USERSET_OBJECT" relation: "viewer" }
}}
}
}
}A API: 4 verbos que bastam
| API | Pergunta | Latência alvo |
|---|---|---|
| Read / Write | CRUD em tuplas — alterar fatos | Spanner consistente, ~10ms |
| Check(user, rel, obj) → bool | usuário pode? — single decision | p95 < 10ms global |
| Expand(obj, rel) → userset tree | quem tem essa relação? — debug | p95 < 50ms (recursivo) |
| Watch(namespace) → stream | mudanças em tempo real — cache invalidation | streaming Spanner |
Note o que NÃO existe: avaliação de policy escrita em DSL. Zanzibar não tem Rego/Cedar. A "policy" está no schema do namespace (rewrites). Para condições dinâmicas (ABAC), você combina Zanzibar com OPA/Cedar — não é "substituição", é complemento.
O 'new enemy problem' e os Zookies
Cenário: Alice tem doc compartilhado com Bob. Alice remove Bob, depois adiciona conteúdo confidencial. Sem garantias de ordenação, um read de Bob pode usar cache stale (ainda com Bob como editor) e ler o novo conteúdo. Esse é o new enemy problem.
A solução naïve seria fazer todo read globalmente consistente — caro. Zanzibar inventou os Zookies: token opaco que codifica um Spanner timestamp. Cliente recebe zookie no write e envia no read; engine garante "reads ≥ esse momento". Custo amortizado ~zero.
// Padrão de uso aplicacional
// 1. Write retorna zookie
const { zookie } = await zanzibar.write({
tuple: 'doc:readme#editor@user:bob',
op: 'delete',
});
// 2. App propaga zookie junto com o doc atualizado (cookie, header, db field)
await db.docs.update({ id: 'readme', content: secret, authz_zookie: zookie });
// 3. Próximo read SEMPRE envia zookie — garante consistência ≥ momento do delete
const allowed = await zanzibar.check({
user: 'bob', rel: 'viewer', obj: 'doc:readme',
consistency: { at_least_as_fresh: doc.authz_zookie }
});
// allowed === false, mesmo que a réplica local ainda tivesse a tupla antigaArquitetura interna: Spanner + Leopard + aclservers
Performance no paper (mídia comum)
Esses números foram o "reality check" do paper: ReBAC funciona em escala Google, não é só teoria. SpiceDB e OpenFGA não chegam a 10M QPS, mas alcançam sub-10ms p99 em escalas SaaS típicas (milhares de QPS por tenant).
O que os filhos do Zanzibar herdaram
| Sistema | Mantém do paper | Diverge |
|---|---|---|
| SpiceDB | Schema language, tuplas, check/expand, zookies (ZedTokens) | Postgres/CockroachDB como storage; sintaxe própria de schema |
| OpenFGA | Tuplas, namespaces, modeling, check | Adiciona conditional tuples (ABAC), mais simples, foco em DX |
| Auth0 FGA | OpenFGA gerenciado | SaaS-only, multi-region cloud-hosted |
| AWS Verified Permissions | Conceito de policy-as-data + per-resource | Cedar (não Zanzibar — política como código), schema separado |
Perguntas frequentes do paper
❓ Zanzibar substitui banco de dados?
❓ Como Zanzibar lida com ABAC (atributos dinâmicos)?
❓ Por que zookies em vez de simplesmente strong reads?
❓ ReBAC torna RBAC obsoleto?
Resumo executivo
- Zanzibar (Pang et al, USENIX ATC 2019) é o paper-base de ReBAC moderno. Leitura obrigatória.
- Insight central: autorização vira dado (tuplas obj#rel@user), não regra estática.
- Userset rewrites (union/intersection/computed/tuple-to-userset) modelam herança e composição declarativamente.
- APIs: Check / Expand / Read-Write / Watch. Sem DSL de policy — combina-se com OPA/Cedar para ABAC.
- Zookies (tokens com Spanner TS) resolvem o "new enemy problem" sem custo de strong reads em todo path.
- Performance produção Google: ~10M QPS, p99 ~20ms global, 99.999% disponibilidade durante anos.
- Filhos: SpiceDB, OpenFGA (próximos módulos). Cedar diverge — policy-as-code, não data.