Authorization
Authorization providers store relationship data and answer access checks for Gestalt. Authorization is separate from Authentication, which identifies the caller, and External Credentials, which provide upstream credentials for configured connections.
Gestalt’s model follows the core ideas from Google Zanzibar : subjects, resources, relations, actions, and relationship tuples.
Core Concepts
Subjects
A subject is the caller being checked. Gestalt resolves each request to a
canonical subject, such as user:<id>, service_account:<id>, or a system
subject, then evaluates access for that subject.
For non-human automation subjects, see Service Accounts.
Resources
A resource is what the subject wants to access, e.g.:
external_identity:google-123managed_subject:deploy-bot
Relations
A relation is a named link from a subject to a resource. For example, triage
on an app grants triage membership; admin on a policy grants admin
membership.
Relationship Tuples
A tuple stores one relation:
<subject> <relation> <resource>Examples:
user:alice viewer app_static:github
user:bob editor app_static:githubThe configured authorization provider stores these tuples and uses them as the source of truth for dynamic access.
Actions
Relations are memberships; actions are permissions. The authorization model
defines which actions each relation grants. To answer “can user:alice read
this operation?”, the provider checks whether Alice has a relation on the
resource that grants read.
Static And Dynamic Grants
Static grants live in authorization.policies. They load at startup, do not
change at runtime, and take precedence over provider-backed grants.
Dynamic grants live in the selected authorization provider. Admin APIs can add
or remove them without restarting gestaltd.
If static and dynamic grants both match the same subject and resource, static
wins. For example, if config grants Alice admin on the admin policy, Gestalt
does not ask the provider for Alice’s admin role.
Grant Dynamic App Access
Use the gestalt authorization CLI to grant app access after the server is
running. Built-in Gestalt admins can manage every app. App admins can
manage members for apps they administer.
gestalt authorization apps list
gestalt authorization apps members list github
gestalt authorization apps members set github \
--email operator@example.com \
--role viewer
gestalt authorization apps members set github \
--subject-id service_account:release-bot \
--role editorCreate service account subjects first, then grant those subjects app roles:
gestalt authorization subjects create release-bot \
--display-name "Release Bot"
gestalt authorization subjects grants set service_account:release-bot github \
--role viewerDeployments that split public and management listeners may need --url set to
the management base URL for these admin commands.
Where Gestalt Uses Authorization
Gestalt checks authorization before serving protected surfaces:
- App and operation invocation
- Admin APIs and UI routes
- External identity assumption
- Managed subjects and service accounts
- Workflow and agent execution grants
Authentication, operation scope, authorization, and credential selection are separate checks. Passing one does not imply the others.
Provider Contract
Authorization providers implement three groups of RPCs.
| Group | Methods |
|---|---|
| Decisions | Evaluate, EvaluateMany, SearchResources, SearchSubjects, SearchActions, GetMetadata |
| Relationships | ReadRelationships, WriteRelationships |
| Models | GetActiveModel, ListModels, WriteModel |
EvaluateMany is important for latency. Gestalt batches role checks instead of
calling Evaluate once per possible role.
Optional methods advertise capabilities through GetMetadata. Effective search
is paired: providers must support and advertise both methods or neither.
| Capability | Methods |
|---|---|
effective_search_resources, effective_search_subjects | EffectiveSearchResources, EffectiveSearchSubjects |
expand | Expand |
If an optional method is not implemented, the SDK returns UNIMPLEMENTED.
Configure
server:
providers:
indexeddb: main
authorization: indexeddb
providers:
indexeddb:
main:
source:
package: github.com/valon-technologies/gestalt-providers/indexeddb/relationaldb
version: 0.0.1-alpha.1
config:
dsn: ${DATABASE_URL}
authorization:
indexeddb:
source:
package: github.com/valon-technologies/gestalt-providers/authorization/indexeddb
version: 0.0.1-alpha.1
config:
indexeddb: mainThe first-party IndexedDB authorization provider stores models and relationship tuples in a configured IndexedDB provider.
Use The Host Client
Provider code can query the selected authorization provider through the SDK host client.
Go
import (
"context"
gestalt "github.com/valon-technologies/gestalt/sdk/go"
)
func canJoinTeam(ctx context.Context, req gestalt.Request, userID string) (bool, error) {
authz, err := req.Authorization()
if err != nil {
return false, err
}
defer authz.Close()
decision, err := authz.Evaluate(ctx, gestalt.NewAccessEvaluationRequest(
gestalt.NewAuthorizationSubject(gestalt.AuthorizationSubjectTypeSubject, userID),
gestalt.NewAuthorizationAction("join"),
gestalt.NewAuthorizationResource("team", "support"),
))
return decision.Allowed, err
}Build A Provider
An authorization provider manifest uses kind: authorization:
kind: authorization
source: github.com/your-org/authorization/example
version: 0.0.1
displayName: Example Authorization
description: Custom authorization provider.Authoring support is available in Go, Python, Rust, and TypeScript.
Go
import (
"context"
gestalt "github.com/valon-technologies/gestalt/sdk/go"
)
type Provider struct{}
func New() *Provider { return &Provider{} }
func (p *Provider) Evaluate(context.Context, *gestalt.AccessEvaluationRequest) (*gestalt.AccessDecision, error) {
return &gestalt.AccessDecision{Allowed: true, ModelId: "model-1"}, nil
}
func (p *Provider) EvaluateMany(ctx context.Context, req *gestalt.AccessEvaluationsRequest) (*gestalt.AccessEvaluationsResponse, error) {
out := &gestalt.AccessEvaluationsResponse{
Decisions: make([]*gestalt.AccessDecision, 0, len(req.Requests)),
}
for range req.Requests {
out.Decisions = append(out.Decisions, &gestalt.AccessDecision{Allowed: true, ModelId: "model-1"})
}
return out, nil
}Production providers should implement every required RPC. The snippets above focus on the SDK entrypoints and batch-evaluation shape.
Operational Notes
server.providers.authorization selects the active provider. If multiple
entries exist under providers.authorization, set the server provider name or
mark one entry default: true.
Static policy members still win over dynamic grants. Gestalt also writes managed models and relationships into the provider for built-in admin, plugin, external-identity, managed-subject, workflow, and agent authorization.
When OTLP tracing is enabled, keep the incoming request context attached to downstream calls so provider work stays in the same trace.
Next
For exact config fields, see Config File. For package publishing, see Releasing provider packages. For credentials, see External Credentials.