First-run Vault Bootstrap and Permissions
This reference defines the Service Lasso first-run path from an empty workspace to normal runtime. It ties the local vault bootstrap, local and remote auth boundaries, actor resolution, action permissions, and audit requirements into one operator-facing contract.
The related implementation trail is:
- #822: action permissions and audit identity model epic.
- #823: first-run setup mode and local vault bootstrap.
- #824: superseded generated-key reveal proposal; retained for history only.
- #825: local vs remote authentication rules.
- #826: action permission enforcement middleware.
- #827: ZITADEL actor and role mapping.
- #1022: real Broker bootstrap and production resolution.
- #1024: metadata-only public setup responses.
Scope
Service Lasso owns the workspace Broker lifecycle; it does not invent a browser-visible vault-owner credential. The host OS account is useful local context for protected storage, IPC identity, diagnostics, and audit, but it is not a remotely transferable product identity.
The local runtime owns setup-mode detection, vault readiness, normal autostart blocking, safe setup audit events, and action permission checks. Services that own identity providers, broker storage, OIDC callbacks, or provider-specific session behavior keep those responsibilities outside core.
Empty workspace flow
An empty workspace starts in setup mode until protected Broker runtime credentials, the encrypted store, and the platform key-custody evidence all agree. A legacy marker file never satisfies readiness. The setup status contract is exposed by:
GET /api/setup/status
The status response reports:
- setup state:
setup_required,setup_in_progress,setup_complete,setup_failed, ornot_required; - whether the Broker vault is required and ready (never its filesystem path or IPC address);
- the bind host and whether setup is local-only or remote-token gated;
- blockers such as
setup_token_required_for_remote_bind; - operator context, including the host OS username as context only and
identitySource: "vault".
Normal service autostart must wait while setup mode is active. Provider preparation, manifest discovery, and diagnostics can still run when they are safe and non-mutating, but baseline service startup must not proceed until the protected Broker custody is ready.
Launch prerequisite contract
The normal launch path treats setup as a prerequisite phase before managed services are allowed to start. Startup resolves the setup status first, then uses that decision for both CLI output and runtime API state:
- Discover the workspace and service roots.
- Read protected Broker credential, encrypted-store, and key-custody status.
- If setup is required, prepare only the setup-capable dependency set:
@node,@secretsbroker, and@serviceadmin. - Expose setup mode through
GET /api/setup/statusand show the local setup URL from the runtime host and port when an interactive operator can complete setup. - Accept a setup token only as transient authorization for remote bootstrap; never as Broker key material.
- Initialize protected Broker credentials, the encrypted local store, the authenticated local transport, and declared Broker-generated secrets.
- Record setup decisions and denials as metadata-only audit/history events.
- Re-read setup status after bootstrap; when setup is
not_required, continue the regular managed service launch set in dependency order.
While setup mode is active, install and config reconciliation may still run for services that are safe to prepare, but normal service process start is skipped with a setup-mode reason. Existing workspaces skip setup mode only when protected credentials and the encrypted Broker store prove ready together.
This split keeps first-run prerequisites visible without granting temporary admin authority to every managed service. After setup, regular runtime permission checks decide which authenticated actors can install, configure, start, restart, import, restore, or rotate workspace resources.
Vault bootstrap
The first setup action requires the release-backed @secretsbroker artifact to
be installed and configured, then creates and verifies its local custody:
POST /api/setup/bootstrap
Core generates Broker API, launch-signing, and master-key material internally. On Windows, the private credential envelope and Broker master-key wrapper use CurrentUser DPAPI plus current-user/SYSTEM-only ACLs. On Unix-like systems, private credential state and Broker transport files are owner-only and the local IPC transport is bound to the current UID. These credentials never enter command-line arguments or browser responses.
There is no generated-key reveal, copy, download, print, or acknowledgement flow. Public bootstrap responses contain setup state, Broker readiness, safe operator context, trust flags, auth metadata, and the count of provisioned declarations only. Store paths, IPC addresses, tokens, signing keys, master keys, wrapper data, credentials, and secret values remain internal.
If the vault key is lost and no backup or recovery path exists, Service Lasso cannot recover encrypted local secrets. The safe recovery paths are restoring a known-good workspace backup, re-bootstraping a new empty workspace, or reconnecting providers and re-entering secrets through their owning setup flows.
Headless and automation bootstrap
Automation, container, and headless runs do not depend on a UI reveal. They must supply setup authorization through a configured source that can be audited without logging values.
Accepted source classes are:
SERVICE_LASSO_SETUP_TOKENfor gated remote bootstrap during setup mode;- a local loopback setup session under the runtime-known OS operator boundary;
- preconfigured release/install state for
@secretsbrokerin image or automation workflows.
Logs and API responses may report the source class or safe correlation metadata. They must not include paths, raw key values, setup tokens, provider tokens, private keys, session cookies, passwords, or recovery material.
Local and remote auth rules
0.0.0.0 is a bind address, not a browser origin. Loopback origins are
127.0.0.1, ::1, and localhost (plus other 127.0.0.0/8 client addresses).
Opening Admin as http://192.168.x.x:17700 or a hostname is remote even when
Core or Admin listen on 0.0.0.0.
Loopback is local-root without a password at the Core request-policy layer.
The first Admin visit still forces the operator to copy and save the local-admin
token and local-operator password (SPEC-005 AC-5J). After that
acknowledge, later Admin visits require token or password login. Loopback keeps
an explicit Continue as local-root break-glass control (session-only).
Loopback still allows Lasso-local password, vault token, and SSO/ZITADEL when
configured, including when vault flag runtime/auth / FORCE_SSO is true, so a
bad flag cannot brick the machine or hide break-glass methods. Flip FORCE_SSO
from loopback via the KV editor.
First-run writes Lasso-local secrets into Broker KV path runtime/local-operator
(LOCAL_OPERATOR_USERNAME, LOCAL_ADMIN_TOKEN, LOCAL_OPERATOR_PASSWORD)
before the one-time loopback envelope is written, so INIT never shows
credentials that are not already in the vault. Copy/save on INIT remains the
operator backup. GET /api/runtime/security reports firstRunPending without
credential material. While pending but the envelope is not yet written, loopback
GET /api/runtime/auth/first-run returns 503 first_run_vault_not_ready
without secrets. Values are not stored in service.json. operator.json stays
the Broker daemon token and is not the operator login token. Workspaces that
already have hashed local-operator state marked acknowledged (or legacy files
with the acknowledgement field missing) cannot re-show a token that is no
longer in plaintext.
Remote login (when FORCE_SSO is off) is either the vault-retrieved token,
username local-operator plus the Lasso-local password via
POST /api/runtime/auth/local, or SSO when configured. Service Lasso must not
collect OS or Windows passwords in a web form. Remote failures are rate-limited;
loopback is not.
When FORCE_SSO is on, remote access requires a ZITADEL actor. Local and token
proofs are disabled remotely. Loopback continues to allow every configured
method. Traefik OIDC cutover remains a later phase. Prove this HTTP matrix with
npm run verify:auth-e2e (SPEC-005 AC-5I, AC-5J). Live ZITADEL browser SSO is not
part of that gate.
The local admin token is secret material. It must not appear in audit payloads, diagnostics, telemetry, issue comments, or PR bodies.
Actor model
Every mutating or sensitive action should resolve to one of these actor kinds:
| Actor kind | Source | Notes |
|---|---|---|
local-root | Runtime-proven loopback request | Administrative only inside the exact local trust boundary. |
local-token | Explicit configured local admin token | Emergency/automation authority; never browser storage. |
zitadel-user | ZITADEL-backed user session | Identity is proven by ZITADEL; Service Lasso maps it to a workspace actor. |
service-account | Explicit service identity | Used by automation and services such as workflow runners. |
system | Runtime-owned scheduler, supervisor, or health repair | Must carry a stable system actor id and reason. |
ZITADEL proves who the user is. Service Lasso decides what that actor can do. ZITADEL subjects, groups, roles, and safe display metadata map to Service Lasso users, linked identities, workspace roles, and entitlements through the platform facade. Raw ZITADEL tokens, callbacks, cookies, and provider credentials do not belong in core runtime state.
System actors such as scheduler, supervisor, and health repair are not implicit admins. They can run only the bounded operations assigned to their service identity or internal system role, and their audit records must say why the runtime acted.
Service account actors such as Dagu or other workflow runners must be explicit service identities. They receive named entitlements for the relevant workspace and instance. A service account cannot borrow a human user's identity or bypass broker policy.
Permissions
Roles are workspace-scoped bundles of entitlements. Core entitlements include:
workspace:readworkspace:adminsecrets-broker-source:readsecrets-broker-source:writesecrets-broker-source:usesecrets-broker:resolveworkflow:run
Permission checks must fail closed when the actor, workspace, role mapping, entitlement, service identity, or target state is missing or inactive.
Durable HTTP routes resolve the actor from the trusted request-policy identity
(local-root, local-token, or zitadel-user). JSON bodies may carry
workflow metadata, but they must not supply actor authority. Unmapped ZITADEL
actors authenticate for reads when identity is proven, then fail closed for
mutating action runs until workspace grant mapping is applied. In-process
callers use explicit factory actors: runtime-recovery-monitor (service:restart),
runtime-update-scheduler (service:update), and cli-local-root (local CLI
operator mutations). HTTP cannot spoof those kinds. Sensitive in-process
restart and scheduled install use policy elevation; ungranted or unconfirmed
calls fail closed without mutation and still write a metadata-only permission
decision to the existing audit store (SPEC-002 AC-4CC). Leftover HTTP
durable mutations for update check/download/install, setup run, recovery
doctor, and runtime startAll/stopAll/autostart/reload resolve the trusted
request-policy actor, call the same helper before mutation, and never treat
JSON-body actor claims as authority (SPEC-002 AC-4CD).
Dangerous or elevated actions need confirmation even when the actor has a base entitlement. Examples include service restart, destructive config apply, restore, migration, vault rotation, provider disconnect, or service import. The confirmation must be based on a fresh plan and must be bound to the same actor, target, capability fingerprint, and expiry window before execution.
Audit minimum
Audit is metadata evidence, not a secret store. Every setup, permission, and elevated-action decision should record at least:
- event id, timestamp, contract version, and source;
- actor kind, actor id, workspace id, and instance id when known;
- action name, subject type, and subject id;
- outcome:
success,failure,denied, orskipped; - permission decision and missing entitlement or denial reason when applicable;
- auth method, such as
local-root,setup-token,zitadel-session,local-admin-token,service-identity, orsystem; - safe correlation ids, plan ids, confirmation ids, route templates, and status codes where available.
Audit must never include raw vault keys, setup tokens, local admin tokens, bearer tokens, OIDC tokens, session cookies, provider credentials, raw secret values, private keys, passwords, raw request bodies, raw terminal input, or recovery material.
Normal runtime invariant
After bootstrap is complete:
- the Broker vault, protected credential state, and authenticated IPC probe are ready;
- setup mode is disabled for normal startup;
- local requests are still scoped by the local trust boundary;
- remote requests require the configured login boundary;
- ZITADEL claims map to Service Lasso actors and entitlements before any permission decision;
- service accounts and system actors use explicit identities;
- elevated actions require fresh plan-bound confirmation;
- setup, permission, confirmation, and denial paths emit metadata-only audit evidence.
See also Core Boundary Facade, Startup Broker Resolution, Operator Command Facade, and Audit.