Multi-tenant

What this layer does

Phase 14 carves the substrate into tiers: public canonical (everyone pulls), per-tenant private state (only the owning tenant), client work, and subscriber-tier content. The Worker derives tenant_id from SNAPPY_MASTER_KEY on every request and enforces the tier boundary server-side. Cross-tenant PID still works because the aggregate is anonymized and gated by quorum.

Files involved

SNAPPY_MASTER_KEY.

single-use, 7-day TTL.

derives tenant from key, checks tier grants.

per-tenant tier grants (public always; personal/client/subscriber per row).

promotion.

Tier model

TierPrefixAccess
Publics3://robert-storage/snappy-os/All tenants pull, only canonical author writes
Public kernels3://robert-storage/snappy-kernel/Same
Personals3://robert-storage/snappy-os-tenants/<tenant_id>/Only owning tenant reads + writes
Clients3://robert-storage/snappy-os-clients/<client_slug>/Only granted tenants per tenants/<id>.json
Subscribers3://robert-storage/snappy-os-subscriber/Only tenants with subscriber: true grant
Stagings3://robert-storage/snappy-os-staging/<skill>/<rev_id>/Public read, write per quorum logic

tenant_id derivation

tenant_id = sha256(SNAPPY_MASTER_KEY).first(12_hex)

Worker computes on every request from the auth header. Client-supplied tenant_id is never trusted. Loss of SNAPPY_MASTER_KEY means loss of tenant identity; recovery is via Robert-minted single-use invite code that re-binds the new key to the prior tenant_id (manual approval).

Cross-tenant PID

Per Phase 7: anonymized aggregate (evals.aggregate.ndjson) ships to the public-tier prefix. Quorum logic reads aggregates across tenants and promotes a staged rev when ≥3 tenants score ≥0.85 across ≥5 runs. No tenant sees another tenant's raw evals — only the rolled-up tenant ID hash.

Operational gotchas

tenant_id field in the request body is ignored; Worker recomputes from the auth header on every write.

docs, status, and changelog are all reachable anonymously.

subscriber" flow in v1 — the tenants/<id>.json file is updated by Robert on confirmed payment / acceptance.

INVITES. After redemption they're deleted; expired codes return 410 Gone.

skill should not appear in the public catalog row even though its bytes live in the same bucket.

How to verify it's working

rejected on _push.

(HTTP 403, no body).

given key.

second use returns 410.

public-tier write within 60s.