Skip to main content

Core Boundary Facade Contract

Service Lasso core is limited to Service Manager and Secrets Broker. This facade is therefore not a product account platform, provider-account API, app auth system, or custom OIDC/session/callback/token implementation. It defines only the safe metadata shapes core needs to wire service lifecycle decisions, Secrets Broker refs, policy/audit checks, and workflow/run handoff decisions.

User, workspace, linked identity, and role records in this document are minimal safe context fixtures consumed by core after an app-owned identity service has authenticated the request. ZITADEL, Traefik, Service Admin, and other consuming services own their auth mechanics and provider-specific behavior.

Provider connection records are Secrets Broker source/ref metadata records, not general third-party account lifecycle records. Provider secret payloads, OAuth access tokens, refresh tokens, API keys, private keys, password values, portable master keys, session cookies, callback payloads, and recovery material must not be stored or returned by the facade. Store secret material behind a Secrets Broker or source-backend reference and return only the safe reference metadata needed to authorize and diagnose access.

Data model​

users​

A user is a safe internal actor reference consumed by core after app-owned authentication succeeds.

FieldNotes
idStable internal user id such as usr_....
displayName / emailDisplay metadata copied from a verified identity claim where available.
statusactive, disabled, or pending. Disabled users fail authorization.
linkedIdentityIdsLinks to external identities that can authenticate as this user.
createdAt / updatedAtAudit timestamps.

workspaces​

A workspace scopes safe actor references, Secrets Broker source metadata, broker authorization, and workflow/run handoff metadata.

FieldNotes
idStable internal workspace id such as wks_....
slug / displayNameOperator-facing metadata.
statusactive, suspended, or archived. Suspended/archived workspaces fail mutating actions.
ownerUserIdInternal owner user id.
createdAt / updatedAtAudit timestamps.

linked_identities​

A linked identity records safe identity metadata supplied by an app-owned identity service; core does not authenticate, callback, or refresh the identity provider session.

FieldNotes
idStable link id.
providerzitadel, github, google, telegram, custom-oidc, or another registered provider.
issuerOIDC issuer or provider authority.
subjectProvider subject id.
userIdInternal user id.
workspaceIdsWorkspaces this identity can enter after role checks.
claimsSafe claim metadata only: email, preferred username, groups.
createdAt / lastSeenAtAudit timestamps.

request_context​

Every broker/admin/workflow handoff receives a canonical request context instead of re-parsing cookies, tokens, route parameters, or provider-specific auth state in core.

FieldNotes
userIdInternal user id for a ZITADEL-backed user request, or service id for a service-authenticated request.
workspaceIdActive workspace resolved from request routing/defaults and verified against identity membership.
instanceIdLocal Service Lasso instance boundary. Requests cannot cross instances.
linkedIdentityIdZITADEL linked identity id for user requests, or service identity id for service-authenticated requests.
entitlementsWorkspace-scoped grants flattened from roles or service identity policy.
roleIds / roleNamesRole evidence used to match explicit permission_grants.
permissionGrantsExplicit grants resolved for the actor in this workspace. Empty means default deny for scoped resources.
actorSafe actor descriptor: kind, id, and display label only.
authMethodzitadel-session or service-identity.
auditSafe actor/workspace/instance metadata for downstream audit events.

The resolver returns fail-closed states for unauthenticated, unauthorized, expired-session, workspace-mismatch, service-identity-denied, disabled-user, and workspace-inactive. These responses may include safe actor/workspace/instance ids when known, but never cookies, session secrets, bearer tokens, provider credentials, raw secret values, key material, or recovery material.

service_identities​

Service identities let local system actors such as @node, @serviceadmin, or workflow runners call broker/admin APIs without pretending to be provider OAuth users.

FieldNotes
idStable internal service identity id.
serviceId / displayNameService actor metadata safe for logs and audit.
instanceIdsAllowed Service Lasso instances.
workspaceIdsWorkspaces the service identity may access.
entitlementsExplicit grants such as secrets-broker:resolve; no implicit admin rights.
statusactive or disabled. Disabled service identities fail closed.
createdAt / updatedAtAudit timestamps.

Service-authenticated requests must provide the expected service id, workspace id, and instance id. The resolver rejects unknown services, disabled services, workspace mismatches, and instance mismatches as service-identity-denied or workspace-mismatch.

provider_connections​

A provider connection record is safe Secrets Broker source/ref metadata for a third-party or service provider integration. It is not a provider account lifecycle object and must be clearly split from secret payloads.

FieldNotes
idStable connection id such as pc_....
workspaceId / ownerUserIdOwnership and authorization scope.
providerProvider key, for example github, stripe, or vault.
kindoauth, api-token, webhook, secrets-broker-source, or custom.
displayNameOperator-facing label.
statusready, needs-auth, expiring, refresh-failed, revoked, disabled, error, or deleted. Deleted records are not listed by default.
accountIdProvider-side account, org, tenant, or installation id, when safe to expose.
scopesGranted/expected provider scopes as labels.
brokerNamespace / secretRefPointer to secret material, never the value itself.
expiresAt / lastRefreshAt / lastVerifiedAtSafe lifecycle timing metadata.
lastErrorSafe diagnostic summary only; no provider response bodies or credentials.
affectedSummarySafe summary of affected serviceIds, broker refs, and workflow ids.
secretMaterialPresentMust be false in facade responses. A true value is a contract violation.
createdAt / updatedAtAudit timestamps.

Forbidden fields in facade provider connection payloads include secret, accessToken, refreshToken, apiKey, privateKey, password, credential, keyMaterial, and recoveryMaterial.

roles and entitlements​

Roles are workspace-scoped bundles of coarse entitlements. Entitlements keep legacy API boundaries readable, but they do not grant service/action/resource access by themselves once scoped grants are present. The first concrete entitlements are:

  • workspace:read
  • workspace:admin
  • secrets-broker-source:read
  • secrets-broker-source:write
  • secrets-broker-source:use
  • secrets-broker:resolve
  • workflow:run

Secrets Broker resolution requires secrets-broker:resolve in the same workspace as the requested broker namespace. Workflow/run resolution requires workflow:run; if a workflow uses provider connection metadata, it also requires secrets-broker-source:use for each referenced Secrets Broker source metadata record.

Permission catalogue and access groups​

The durable security model is owned by Service Lasso, not by a single identity provider. Core exposes a permission catalogue and maps actors into Service Lasso access groups. Identity-provider claims are inputs to explicit mappings; they are not executable permissions by themselves.

The first setup seed must create these built-in groups:

  • Owner: all permissions.
  • Security Admin: users, groups, mappings, local tokens, provider auth, audit/security settings.
  • Service Admin: service install/config/start/stop/restart/update and service configuration.
  • Operator: start/stop/restart, validate, diagnose, backup.
  • Viewer: read-only runtime/service visibility.
  • Backup Operator: create backups and view backup history.
  • Restore Operator: restore backups with confirmation/elevated checks.
  • File Export Operator: browse, archive and export files.
  • Scheduler: run approved scheduled actions only.
  • Service Identity: scoped service/broker access only.

Built-in groups are viewable and resettable. Custom groups contain only permission keys from the Service Lasso catalogue, optional safe scope rules, assigned actors/mappings, and audit history. Unknown provider-specific strings must not be accepted as ad hoc permission names.

Provider mappings are generic. A mapping records provider, claim type, claim value, and target Service Lasso group. For example, provider: "zitadel", claimType: "group", and claimValue: "service-lasso-admins" can map to Security Admin, while a future custom-oidc role can map to Operator through the same model.

Safety rules:

  • Keep the built-in local-root role mapping code-owned; Broker bootstrap does not accept browser-supplied group or permission state.
  • Never allow deleting/removing the last Owner-capable actor.
  • Never allow removing your own final security access without strong confirmation.
  • Permission catalogue changes are code/schema-owned, not ad hoc UI strings.
  • Group/mapping changes are audited.

zitadel_role_mappings​

ZITADEL claims are inputs to an explicit Service Lasso mapping, not direct runtime permissions. A mapping record contains:

FieldNotes
workspaceIdWorkspace where the mapping is valid.
claimgroup or role; other raw claims are not executable grants.
valueExact safe claim value supplied by the trusted app-owned identity layer.
serviceLassoRoleInternal role name to load from roles, such as owner, admin, operator, viewer, file-export-operator, backup-restore-operator, or service-account.

The resolver merges safe ZITADEL groups/roles from the linked identity record and the current trusted session, resolves them through zitadel_role_mappings, then flattens entitlements only from matching Service Lasso roles in the active workspace. Unmapped groups or roles produce no entitlements and fail closed as unauthorized. This keeps the permission vocabulary in Service Lasso while allowing ZITADEL projects to choose their own provider-side group/role labels.

permission_grants​

Permission grants are the explicit allow list for service/action/resource authorization. The default decision is deny: a request context with no matching grant is rejected even when the actor has a coarse role label. Adding or importing a service registers available actions, but it must not append grants to existing non-owner groups.

Each grant records:

FieldNotes
idStable grant id for audit and review.
workspaceIdWorkspace boundary for the grant.
targetrole, actor, or service target id.
permissionKeyPermission such as service:restart, secrets-broker:resolve, workflow:run, or owner/root wildcard *.
scopeScope type and id. Runtime wildcard runtime/* is reserved for owner/root bootstrap.
constraintsOptional safe structured limits such as time window or max selected paths.
createdBy / createdAtSafe audit metadata for who created the grant and when.
reasonHuman review note.

Supported scope types are runtime, workspace, service, action, file-source, file-path, broker-namespace, export-destination, and backup-artifact. A grant only matches when the workspace, target, permission key, and scope match the requested resource, except for the owner/root wildcard grant.

{
"id": "grant_postgres_admins_restart_postgres",
"workspaceId": "wks_local_demo",
"target": { "kind": "role", "id": "role_postgres_admins" },
"permissionKey": "service:restart",
"scope": { "type": "service", "id": "@postgres" },
"createdBy": "usr_01hzy9operator",
"createdAt": "2026-05-08T11:00:00Z",
"reason": "Postgres Admins may restart only Postgres."
}

Denied authorization decisions include safe evidence such as missing-entitlement, missing-grant, workspace-mismatch, or connection-not-ready. Allowed decisions include the matched grant id, target, permission key, and scope for downstream audit metadata.

service_action_permissions​

Services can register available actions and required permission keys in the core catalogue/resolved service model:

{
"serviceId": "@serviceadmin",
"actionId": "services.restart",
"permissionKey": "service:restart",
"scopeType": "service",
"description": "Restart a managed service through Service Admin.",
"registeredAt": "2026-05-08T10:00:00Z"
}

This registration is catalogue metadata only. It makes the assignment visible to Service Admin or automation, but it does not grant the action to any group, actor, or service identity.

Secrets Broker source metadata API​

The facade exposes CRUD for Secrets Broker source/ref metadata only. These routes intentionally do not create provider accounts, perform OAuth, refresh provider tokens, manage callbacks, or store provider credentials:

GET /api/platform/workspaces/{workspaceId}/provider-connections
POST /api/platform/workspaces/{workspaceId}/provider-connections
GET /api/platform/workspaces/{workspaceId}/provider-connections/{connectionId}
PATCH /api/platform/workspaces/{workspaceId}/provider-connections/{connectionId}
DELETE /api/platform/workspaces/{workspaceId}/provider-connections/{connectionId}

Create request​

{
"workspaceId": "wks_local_demo",
"ownerUserId": "usr_01hzy9operator",
"provider": "github",
"kind": "oauth",
"displayName": "GitHub Actions metadata connection",
"accountId": "github-org-service-lasso",
"scopes": ["repo:read", "workflow:read"],
"brokerNamespace": "workspaces/local-demo/provider-connections/github",
"secretRef": "provider.github.oauth.client"
}

Response​

{
"id": "pc_github_actions",
"workspaceId": "wks_local_demo",
"ownerUserId": "usr_01hzy9operator",
"provider": "github",
"kind": "oauth",
"displayName": "GitHub Actions metadata connection",
"status": "ready",
"accountId": "github-org-service-lasso",
"scopes": ["repo:read", "workflow:read"],
"brokerNamespace": "workspaces/local-demo/provider-connections/github",
"secretRef": "provider.github.oauth.client",
"expiresAt": "2026-05-30T00:00:00Z",
"lastRefreshAt": "2026-05-08T10:04:00Z",
"lastVerifiedAt": "2026-05-08T10:05:00Z",
"affectedSummary": {
"serviceIds": ["@serviceadmin"],
"brokerRefs": ["provider.github.oauth.client"],
"workflowIds": ["wf_release_checks"]
},
"secretMaterialPresent": false,
"createdAt": "2026-05-08T10:00:00Z",
"updatedAt": "2026-05-08T10:05:00Z"
}

Delete removes the metadata record from the facade response set and emits a safe audit event; deletion of broker secret payloads remains a separate Secrets Broker operation. The response is safe to log because it contains reference metadata only. The facade must never return raw provider secrets, access tokens, refresh tokens, API keys, password values, private keys, key material, or recovery material.

Secrets Broker source metadata lifecycle API​

Core lifecycle actions are metadata/status contracts for Secrets Broker source/ref records. They do not run provider-specific OAuth, reconnect, callback, token refresh, provider account setup, or session logic. Services and packages own those provider flows and may report safe status back to core.

The metadata refresh/test operations let consumers verify stored safe metadata, source availability, and scope posture without mutating provider secret payloads. When provider-owned action is required, core returns actionable setup-needed, source-auth-required, or provider-unavailable metadata so UI and services can hand off without embedding provider behavior in core.

POST /api/platform/workspaces/{workspaceId}/provider-connections/{connectionId}/record-source-auth-required
POST /api/platform/workspaces/{workspaceId}/provider-connections/{connectionId}/record-reconnect-required
POST /api/platform/workspaces/{workspaceId}/provider-connections/{connectionId}/refresh-metadata
POST /api/platform/workspaces/{workspaceId}/provider-connections/{connectionId}/test-metadata
POST /api/platform/workspaces/{workspaceId}/provider-connections/{connectionId}/disable-metadata
POST /api/platform/workspaces/{workspaceId}/provider-connections/{connectionId}/disconnect-metadata

Normalized lifecycle statuses are:

  • connected
  • expiring
  • refresh_failed
  • reconnect_required
  • revoked
  • permission_changed
  • disabled
  • source_auth_required
  • degraded
  • deleted

Lifecycle responses include safe metadata only:

{
"connectionId": "pc_github_ready",
"provider": "github",
"action": "test-metadata",
"status": "connected",
"ok": true,
"auditEvent": {
"id": "audit_provider_ready_001",
"workspaceId": "wks_local_demo",
"connectionId": "pc_github_ready",
"provider": "github",
"action": "test-metadata",
"fromStatus": "connected",
"toStatus": "connected",
"outcome": "success",
"at": "2026-05-08T11:05:00Z",
"actorUserId": "usr_01hzy9operator",
"safeDetail": "Provider metadata check succeeded; no provider credential values returned."
}
}

Unavailable or setup-required actions return actionable errors without provider secret values:

{
"connectionId": "pc_slack_missing",
"provider": "slack",
"action": "record-source-auth-required",
"status": "source_auth_required",
"ok": false,
"error": {
"code": "setup-needed",
"message": "Provider connection needs operator setup before it can be used.",
"action": "Hand off to the owning service/package setup flow; core records metadata only.",
"provider": "slack",
"retryable": true,
"documentationRef": "docs/reference/product-api-facade.md#provider-connection-lifecycle-api"
}
}

Each transition records a safe audit event with workspaceId, connectionId, provider, action, fromStatus, toStatus, outcome, at, actorUserId, and a safeDetail. Audit details may mention missing authorization, changed permissions, or unavailable provider support, but must never include access tokens, refresh tokens, API keys, provider secrets, key material, or recovery material.

Reference lifecycle fixtures cover healthy, expiring, missing/setup-required, refresh-failed, denied, revoked, reconnect-required, metadata-disconnected, and deleted states in src/platform/providerConnectionLifecycle.ts. Tests in tests/provider-connection-lifecycle.test.js verify status normalization, secret-safe error payloads, unavailable action stubs, and safe audit transition metadata.

ZITADEL session mapping​

When ZITADEL is used by a consuming app, Service Lasso maps the OIDC session to internal context as follows:

  1. Match issuer + subject to a linked_identities record with provider: "zitadel".
  2. Resolve the internal userId and allowed workspaceIds from that record.
  3. Select the active workspace from request routing or the user's default workspace.
  4. Map safe ZITADEL groups/roles through zitadel_role_mappings and load only the matching Service Lasso roles for that workspace.
  5. Produce a request context containing userId, workspaceId, instanceId, linkedIdentityId, actor metadata, auth method, safe audit metadata, and entitlements.
  6. Fail closed if the linked identity, workspace, user, session freshness, mapping, or required entitlement is missing.

Audit metadata may include safe ZITADEL actor context: issuer, subject, groups, roles, linked identity id, resolved internal actor id, and resolved Service Lasso role names. It must not include raw credentials, tokens, cookies, session secrets, or provider response bodies.

ZITADEL remains app/service-owned. This facade contract describes the safe identity metadata core may consume after authentication; it does not make ZITADEL, OIDC sessions, callbacks, or token handling part of the Service Lasso core baseline.

Service-authenticated requests​

Broker-adjacent local services use the same request context shape through a service-identity auth method. The identity must be explicitly allowed for the requested instanceId and workspaceId, and receives only its configured entitlements. This supports local runtime resolution such as @node reading a broker ref without embedding provider credentials or raw secret values in the runtime API.

Service identity failures are intentionally narrow:

  • missing service auth -> unauthenticated / 401,
  • unknown or disabled service -> service-identity-denied / 403,
  • instance mismatch -> service-identity-denied / 403,
  • workspace mismatch -> workspace-mismatch / 403,
  • inactive workspace -> workspace-inactive / 403.

Audit metadata for service requests includes actor kind service, service id, workspace id, instance id, auth method, and service identity id when known. It must not include bearer tokens, local service credentials, provider credentials, raw secrets, cookies, or session material.

Authorization boundaries​

Authorization is workspace-scoped and fail-closed:

  • Secrets Broker source metadata read/write/use checks must compare the request workspace to the connection workspaceId.
  • Secrets Broker checks must compare the request workspace to the broker namespace owner and require secrets-broker:resolve.
  • Workflow/run checks must require workflow:run, then require secrets-broker-source:use for each Secrets Broker source metadata record referenced by the run.
  • Connections with status needs-auth, revoked, disabled, or error cannot be used for broker or workflow authorization.
  • Request context resolution must represent unauthenticated, unauthorized, expired-session, workspace-mismatch, service-identity-denied, disabled-user, and workspace-inactive distinctly so callers can fail closed and show safe recovery guidance.
  • Denied authorization responses may include metadata reasons such as workspace-mismatch, missing-entitlement, or connection-not-ready, but must not include provider secret values, tokens, cookies, session secrets, or key material.

Test fixtures​

The TypeScript contract lives in src/platform/facade.ts. Tests in tests/product-api-facade.test.js verify:

  • required facade model and endpoint concepts exist,
  • ZITADEL session context maps to internal user/workspace context,
  • ZITADEL group/role claims map explicitly to Service Lasso roles,
  • provider connection metadata is split from secret payloads,
  • Secrets Broker and workflow authorization boundaries are testable, and
  • sensitive strings are rejected or absent from fixture/API shapes.