Trust model
sprig’s security argument rests on three identities with sharply bounded rights. Nothing in the system holds broad power; every capability is scoped to the smallest surface that makes the product work.
The identities
Section titled “The identities”1. Builders and viewers: Entra only, zero Azure
Section titled “1. Builders and viewers: Entra only, zero Azure”Builders reach sprig through a Claude connector or Claude Code, authenticated with their normal Entra account: no Azure RBAC roles, no service principals, no secrets, nothing installed on their machine. Viewers sign in the same way, through the gateway’s own OIDC session. Neither can create, modify, or even see an Azure resource. Everything that happens in Azure happens because the platform did it, with every action authorized against, and audited under, their own identity.
2. The platform: separate writer, gateway, and serving identities
Section titled “2. The platform: separate writer, gateway, and serving identities”The control plane, the gateway, and the shared data layer use three separate user-assigned
managed identities, and no runtime attaches more than one. The platform API’s identity can write
metadata and published bundles. The gateway’s identity has Blob read access, a platform API
serving role, and exactly two data-plane write grants: the DataAudit audit table, and the
container holding the key ring that encrypts its sign-in cookie. It also holds wrap and unwrap
on the single key that protects that ring, which is what stops read access to storage from being
equivalent to forging a session. The shared data layer’s serving identity has Blob read and no
write grant anywhere.
Splitting those two apart is deliberate. Azure attaches roles to identities rather than to workloads, so one identity shared between the gateway and the data layer would hand the data layer the cookie key ring and the key that unwraps it: the exact pair needed to forge a session for any user. The data layer has no use for either, so it holds neither.
A compromised serving container therefore cannot mint archive upload grants, change
active-version pointers, mutate bundle files, or reach the cookie key. All three identities
can pull only sprig/* images from the dedicated release registry and cannot list its catalog.
Each identity can read only its own named OBO client secret, not the vault or another
identity’s secret. Source management handles no credential and needs no vault access.
No identity has a subscription-scope role or Application.ReadWrite.All. See
Exact permissions granted for every current role and scope.
The admin console is a same-origin browser surface. The browser holds the normal signed-in session plus an anti-forgery request token kept in page memory. It never receives the delegated platform API bearer. Source changes require the exact admin origin, a JSON request, and a valid anti-forgery token and secure cookie pair. Invalid changes are refused before sprig acquires a delegated token or contacts the control plane. After that check, the gateway verifies the Ops role and calls the platform on behalf of the signed-in operator.
There is no deployment credential on builder machines and no shared secret in a client. For a db-delegated source (the default), neither managed identity is the identity that reads your data (below): no standing credential has data access there at all. A source can instead be registered gateway-enforced (currently only Fabric Warehouse / lakehouse sources): the shared data layer’s own serving identity then reads that source directly, scoped to read-only, with every use audited, an explicit and recorded exception rather than a default.
3. Published apps: gated by Entra groups, not a per-app registration
Section titled “3. Published apps: gated by Entra groups, not a per-app registration”Every published app is checked against a pair of Entra groups (app-<name>-viewers,
app-<name>-builders) on every request, by the gateway, before it serves a single byte.
Until Ops approves a share, only the owner is in the viewers group. This replaced an earlier
design where every app got its own Entra app registration with assignment required: the
enforcement moved from “a registration exists” to “the gateway checks a group,” which is
authorization logic in reviewable platform code rather than a per-app Azure object to audit
individually.
4. The database: the authorization boundary for a db-delegated source
Section titled “4. The database: the authorization boundary for a db-delegated source”For a db-delegated source (the default, and the only mode Azure SQL Database supports), the gateway’s data proxy does not decide who sees which rows: it forwards the caller’s own identity to the database (on-behalf-of), and the database’s own row-level security decides. This is deliberate: even if the gateway had a bug in its app-access check, a query still runs as the real, signed-in caller, against permissions and row-level security defined in the source’s own schema, never a sprig-controlled ACL layered on top.
For a gateway-enforced source (currently only Fabric Warehouse / lakehouse sources), this is inverted by design: the data layer’s own serving identity authenticates to the source, and sprig’s own policies do the per-viewer filtering, because the source’s engine has no delegated-identity path for that source type yet. This is an explicit, audited exception, not a silent one: registering a source in this mode writes an event stating exactly that. See Data access for the full picture.
Why this shape
Section titled “Why this shape”- The agent can’t outrun the user. Claude drives every publish and every query, but the platform authorizes each call against the builder’s or viewer’s own Entra identity for a db-delegated source (the default), never a service account standing in for them; a gateway-enforced source (currently only Fabric) is the one recorded, audited exception, where sprig’s own identity reads and its policies filter per viewer instead.
- No copies of data, anywhere. See Data access: every query streams through the gateway to the source and back, never cached or stored by sprig, regardless of enforcement mode. Sharing an app can never leak data beyond what each viewer could already query themselves.
- Everything attributable. Every platform operation (publish, share, source exposure, every data-path query) writes an audit event naming the actor, the action, and when.
What we ask you to trust, explicitly
Section titled “What we ask you to trust, explicitly”- The installer, which runs under your interactive Ops account and supports a preview mode
showing everything it will create (exact list). Repository
maintenance uses a temporary
sprig/*-conditioned assignment that is audited and revoked on every exit, rather than standing registry credentials. - The platform code deployed into your subscription: the gateway, the control plane, and the data layer’s configuration. It is reviewable before deployment and installed from signed, versioned container images pinned by digest.
- Microsoft Entra ID and Azure SQL’s on-behalf-of token exchange. These are the same primitives the rest of your estate already relies on; sprig adds no parallel identity or ACL system.