Skip to content

Anvil Connect commands

anvil-serving connect manages application access for API clients and browsers. It is the Anvil Connect product family. A local deployment JSON selects fixed resources, component binaries, service identities, and secret references. The existing edge commands continue to manage Tailscale independently.

This delivery initially targets Linux amd64. Native Connect, Caddy, Authelia, and wstunnel run as separate processes. The Python package adds no runtime dependencies and does not download or start those binaries when imported.

Deployment commands require --manifest /absolute/deployment.json. --service selects gateway, connector:ID, or client:ID from that manifest. These are local deployment selectors; the command does not perform implicit SSH or controller dispatch. Mutations preview by default; --confirm applies the operation and --dry-run keeps it a preview even when both flags are supplied.

The standalone anvil-connect-ctl displays help when run without arguments. users lists account operations, and users create --help (or another operation) shows only relevant arguments. Examples put options after the operation; existing option-first user commands remain accepted. Invalid arguments point to the relevant help without echoing private operands. Standalone command results remain JSON, now indented for readability.

Resources

sudo anvil-connect-ctl resources

Lists declared browser resource IDs, URLs, native authentication modes and copyable resource:member / resource:admin values. Use these exact IDs with users create --grant or users access --grant; application names need not match resource IDs. API resources are excluded because browser grants do not authorize API access. Passthrough applications retain their own internal permissions.

The command defaults to /etc/anvil-connect/deployment.json, accepts --manifest, and works identically as anvil-serving connect resources. It reads only the declaration; it does not contact services, read referenced secrets or establish live readiness or current access. A deployment without browser resources returns an empty list.

Users

Follow Invite a developer for password setup, passkey enrollment, terminal connection, and operator-assisted recovery.

Inspect accounts first:

sudo anvil-connect-ctl users list
sudo anvil-connect-ctl users show developer

Both require local root access and return only username, email, groups and disabled status from the checked Authelia file backend. They do not restart services or read factor storage. Output remains private account metadata. Groups and disabled status describe sign-in accounts, not Connect service grants; inspect those through Manage access on the service home. A missing username fails explicitly.

Create an invited developer on the authentication host:

sudo anvil-connect-ctl users create developer --email developer@example.test --grant workbench:member --confirm

The standalone manager and anvil-serving connect users expose the same commands. User administration defaults to /etc/anvil-connect/deployment.json; another installation can select --manifest. Omit --confirm to preview. This Linux command manages the pinned Authelia file backend through its native password hashing and factor-removal commands. Optional authelia.smtp makes Authelia email password-setup links and verification codes directly; see the email configuration.

Creation defaults to role member (Authelia group members). Use --role admin explicitly for group admins. These groups record the intended role; Connect operator privileges still require separate authorization. --grant assigns exact service entitlements and an application role; repeat it for each declared browser service. Omitting grants creates only the Authelia account. Resets preserve existing groups and do not enable disabled accounts.

Replace an existing person's complete service grant list:

sudo anvil-connect-ctl users access developer --grant workbench:member --grant pi:admin --confirm

The command resolves the user's actual Authelia OpenID identifier; a username is not an OIDC subject. Create and access send the validated command username only as account metadata alongside that authoritative subject. Resources omitted from this list are withdrawn. A successful access change enables the Connect account and invalidates existing browser and terminal credentials for the person. API principal grants remain separately declared. For a suspended account, it also re-enables the Authelia account, preserving its password, groups and registered factors, after applying the explicit grant list.

Suspend or delete a person by username:

sudo anvil-connect-ctl users suspend developer --confirm
sudo anvil-connect-ctl users delete developer --confirm

Omit --confirm to preview. Suspension disables the Connect identity and revokes its browser sessions and human-approved terminal credentials, while preserving the password, factors and service grants. Use users access with the intended grants to resume it.

Permanent deletion removes the password-file account, authentication factors, Authelia OpenID identifiers and related OAuth records, and the Connect principal, sessions and human-approved terminal credentials. The username can then be used for a new account with a fresh identity and explicitly assigned grants. Configured Connect operators cannot be deleted; change the operator configuration first. Deletion backs up authentication state before removing it, briefly restarts Authelia if active, and retains a protected progress record if interrupted. Retry the same deletion to finish; an incomplete deletion stays disabled.

Backups, application data and separately issued API keys are retained; these keys have no automatic username association and require separate revocation. An old backup can contain a deleted account: review revocations before recovery. Use suspension when access may be needed again.

With filesystem delivery, the result names an exclusive, root-only handoff file beneath /etc/anvil-connect/handoffs; credentials never appear in command output. Deliver its contents directly to the intended developer, then remove the file. Create and password reset store only the hash of a random bootstrap password; the usable value is discarded and never written to a handoff. The handoff instead contains Authelia's one-time password-setup link, valid for five minutes. With SMTP, Authelia emails this link and no local handoff file is created. The developer opens it and chooses a password before signing in. Authelia must be running to issue the setup link. A newly created account has no passkey, so it must complete this setup before signing in. These checks run before account mutation. Keep the users file owned by the Authelia service identity with mode 0600, so the provider can save the password chosen by the developer. The managed service permits writes to this exact file; existing installations do not need to move it. Authelia still requires an email attribute for identity-verification messages, even with manual delivery through its filesystem notifier.

sudo anvil-connect-ctl users code developer --confirm
sudo anvil-connect-ctl users reset-password developer --confirm
sudo anvil-connect-ctl users reset-mfa developer --confirm

With SMTP, users receive codes directly by email; users code explains this and does not read historical local notifications. With filesystem delivery, after the developer requests an enrollment code or password reset on the Authelia page, code exports only a matching recipient's notification less than five minutes old to a private handoff file. It returns either a verification code or an exact-provider password-reset link. Use this to replace an expired welcome link after the developer requests another reset. The filesystem notifier holds only the latest message; handle invitations one at a time. Export does not prove that a code or link remains unused. reset-mfa explicitly removes that user's WebAuthn/passkeys and TOTP so they can enroll again. Password reset alone retains those factors. Both website login and terminal browser approval continue to use Authelia's existing browser flow.

Password reset preserves groups, grants and factors, invalidates the old password, and revokes Connect browser sessions and human-approved terminal credentials. Pending browser sign-ins begun before the reset are also rejected. After a successful replacement, old create/reset handoffs for that account are removed. If setup-link delivery fails after the password was replaced, the command reports a partial operation; request a fresh reset link rather than relying on the old password. For SMTP the response reports a requested email, not confirmed delivery: Authelia's reset endpoint conceals delivery errors to prevent account enumeration.

Create/password reset briefly stop and restart the running Authelia service, clearing its in-memory sessions. Factor changes and manual backups restart it only if it was already running and do not themselves revoke Connect credentials. Separately issued API keys require their own revocation.

Account files, email addresses, password hashes, registered factors, storage keys, handoff files and their backups are classified restricted-authentication. Keep them outside Git, ordinary logs, shared folders and unencrypted cloud sync. Use only synthetic identities in tests and published evidence. This is a local handling label, not a legal compliance certification.

sudo anvil-connect-ctl users backup --include-gateway --confirm
sudo anvil-connect-ctl users schedule --confirm
sudo anvil-connect-ctl users restore --input /etc/anvil-connect/backups/ARCHIVE.zip --sha256 DIGEST --destination /etc/anvil-connect/backups/recovery --confirm

A backup runs automatically before create/reset/suspend/delete operations and when resuming a suspended account. Run users backup after a developer finishes enrollment or changes their own password/passkeys, and before maintenance. It includes the users file and, when initialized, a consistent SQLite copy plus its storage encryption key. The root-only archive records its classification and member checksums; output includes its SHA-256. Keep that digest separately. The command retains the newest verified snapshot for each of the last 14 UTC calendar days, plus the seven newest event snapshots. It preserves unrecognized, future-dated, and timestamp-mismatched archives. Archives are limited to 64 MiB and are not encrypted. These are local rollback copies; they cannot recover a lost disk. Encrypt any copy taken to another device. Host configuration and other secrets need their own backups; the gateway authority is included by the option below.

Add --include-gateway for the nightly recovery set. It takes the Authelia snapshot first, then stops only the native gateway for its offline authority archive and starts that exact prior gateway again. Caddy and Authelia remain running for that gateway step. The two archives are sequential rather than atomic, and the gateway receipt binds the auth archive digest, gateway archive digest, and native digest. Validated pairs use the same 14-day and seven-recent retention policy. Both archive types are bounded local copies and are unencrypted.

Restore requires the independently retained digest, checks archive and SQLite integrity, then extracts only into a fresh root-only directory. It does not overwrite live accounts or activate recovered factors. Review account/factor revocations before a separately managed recovery activation. An old authentication snapshot can contain credentials that have since been revoked. The existing connect backup/connect restore commands below separately handle gateway authorization state.

users schedule installs an owner-checked systemd service and persistent timer for 03:17 UTC daily. It supports the default manifest and the verified standalone gateway installation under /opt/anvil-connect; install this manager release before enabling it. It uses the same backup operation, including its sequential Authelia and gateway snapshots. The timer catches up after downtime. It does not send email notifications. An existing unrelated unit with the same name is refused.

To enable permanent deletion from the browser, install the verified manager and its fixed worker first:

sudo anvil-connect-ctl users deletion-schedule --confirm

Then enable gateway.gateway.browser_administration.user_deletion in the managed deployment. The Connect home access editor and Workbench access console show Delete user, with the username and full account ID. Self-deletion and deletion of configured operators are refused. Confirmation immediately disables the account; the root worker processes one pending deletion every ten seconds. Refresh accounts to see completion. The worker runs only the fixed users process-deletions --confirm operation and shares the deployment lock with configuration changes. No browser request supplies a command or filesystem path.

Service home and application roles

Set optional gateway.gateway.portal_host to a dedicated hostname such as home.example.test to publish the chooser independently of an application. The gateway serves its root, home assets, and sign-in endpoints directly; it does not create a connector resource or a service grant. Publish that hostname through the verified TLS edge like the existing browser hosts. Rendering adds its exact OIDC callback and makes it Authelia's landing destination. It must be distinct from every application, authentication, control, and tunnel hostname. Its cookie is bound to the home host and cannot authorize application access. Enabled Connect accounts see only their existing service entitlements there. Access editing remains on the configured administration service's own host.

Open /_anvil-connect/home beneath a declared browser service's path prefix. For a service rooted at /workbench, the home is https://workbench.example.test/workbench/_anvil-connect/home. Login returns to that page. It lists only the person's current browser entitlements, links to Authelia account/passkey settings, explains terminal approval, and signs out of Connect. Each service host retains its own Connect cookie; Authelia supplies SSO when opening another tile. An enabled Connect account can open the chooser even without access to the service hosting it. This grants no application, device or administrative access. Those endpoints still require their existing entitlements. Without a dedicated home host, root sign-ins return to the chooser. With portal_host configured, opening an application tile returns to that application after sign-in; its explicit home path remains available for access editing. Links to deeper application pages retain their destination.

A browser resource may declare up to eight fixed external OIDC return endpoints:

"external_redirects": ["https://auth.example.test/api/oidc/authorization"]

Each endpoint is an exact HTTPS host and non-root path on a host different from the browser resource. Connect permits only 302 or 303 responses to a declared scheme, host, and path; the response may add its query parameters.

The operator's home on the configured browser_administration.browser_resource also has an access editor. It uses the existing CSRF-protected, generation-checked administration endpoint. Choose no access, member, or admin for each service; saving invalidates the edited person's sessions. Account IDs are opaque Connect identifiers. Use the username-based CLI when identifying a new person. To suspend the final service, disable the account; the authority retains a nonempty grant list. The last Connect operator cannot be removed. Application admins do not become Connect operators or inherit other services, and new services are never granted automatically. A browser gateway resource may also set an optional display_name for chooser tiles and access-editor labels. It does not change rule.id, which remains the authority key for grants, roles, sessions, enrollment, and transport.

For Workbench/Observatory to enforce these roles inside the app, declare native_auth: signed-identity at the Connect browser resource and configure the receiver's authentication.connect.roles, for example:

{"member":"workbench-member","admin":"workbench-admin"}

Each value names an existing application principal with explicit resources and actions in the application's users configuration. A signed role without a matching receiver mapping is denied. When roles is configured, assertions without a role are also denied, even if a legacy subject mapping exists. Assign roles to existing users before enabling this receiver mode. Receivers that omit roles retain the existing subject-based behavior. A role downgrade changes the signed request identity and invalidates previous sessions. Passthrough applications, including a separate Pi deployment without a Connect identity adapter, still manage their own roles; their editor says that explicitly and their tiles omit the role badge. Merely storing a role label does not grant or restrict an action inside such an application.

Authelia 4.39.20's file backend has no forced-first-login-change or annual-expiry flag. Connect requires initial password setup by withholding the discarded bootstrap password and using Authelia's supported reset flow. When passkey login is enabled, reset preserves registered factors; with the experimental verified-passkey-as-two-factors option, a registered qualifying passkey can continue passwordless sign-in. The reset link establishes the new password before password sign-in. Annual password expiry is not enabled. The managed passkey profile disables remembered sessions and retains its five-minute inactivity and one-hour expiry limits. Passkey-only login satisfying two factors is experimental.

Root Observatory with Grafana

A browser resource rooted at / can use the managed Caddy origin mux when a single authenticated host must serve Observatory and Grafana. The mux is loopback-only and uses longest-prefix routing. Its required / fallback points to Observatory and is the only route that may set preserve_identity: true; that resource must use native_auth: signed-identity. Route /grafana points to Grafana with preserve_identity: false, so Connect identity assertions do not enter Grafana. Keep Grafana's own authentication URL loopback-only and use relative /grafana/... links so browser navigation stays behind the same Connect resource.

For Observatory's optional fallback login, fallback_authentication.origin is an exact HTTPS origin distinct from the Connect origin, and fallback_authentication.grafana_url is a loopback HTTP(S) URL. Its users list may contain only existing allowlisted native user IDs; it does not create accounts or widen Connect access.

Qualify

Run the unattended Linux browser baseline with saved qualification settings:

anvil-serving connect qualify --lane baseline

The default settings file is ~/.config/anvil-connect/qualification.toml. Use --config /absolute/qualification.toml to select another file. This is a separate test configuration: it contains source, artifact, dependency-cache and preinstalled tool paths, without production identities or credentials. It does not select a deployment manifest or contact a model endpoint.

Save these settings once, replacing the example paths with prepared local paths. playwright_root contains the pinned node_modules directory; go_module_cache contains the already downloaded Go modules. Select actual executables, not symlinks. The artifact directory must be owned by the test user and mode 0700; the command can create it beneath a writable parent.

schema = "anvil-connect.qualification-config/v1"
source_root = "/srv/anvil-serving"
artifact_root = "/srv/connect-qualification/results"
playwright_root = "/srv/connect-qualification/dependencies"
go_module_cache = "/srv/connect-qualification/go-modules"
timeout_seconds = 600

[tools]
go = "/opt/go/bin/go"
node = "/opt/node/bin/node"
chromium = "/opt/chromium/chrome"
certutil = "/usr/bin/certutil"
caddy = "/opt/connect-tools/caddy"
authelia = "/opt/connect-tools/authelia"
wstunnel = "/opt/connect-tools/wstunnel"

The baseline runs the existing Caddy, Authelia, wstunnel and Chromium fixtures with synthetic accounts and isolated state. It checks certificate rejection before trusting the fixture CA, authenticated browser access, and the fixture's logout/revocation behavior. Required binaries are checked against recorded pins; missing prerequisites fail preflight. The command does not install dependencies.

Results distinguish passed, failed, skipped and not-run tests. Preflight failures report both tests as not-run. A failure after execution begins reports only known counts; counts: null means no complete test result is available. Cleanup failure keeps any completed test counts but still fails the qualification. Skipped tests never qualify the lane, and an unexecuted second test is explicitly not-run. The private result artifact records revisions, tool identity, test outcomes and cleanup evidence. A failed qualification exits nonzero. Raw authentication responses and credential-bearing browser diagnostics are excluded from output. Failed test evidence includes a safe failure-stage label. Each run keeps result.json, evidence.json, junit.xml and SHA256SUMS after removing its temporary source copy, profiles and caches. The source checksum binds the tracked Connect files actually staged for that run; untracked files are excluded.

This baseline does not qualify virtual passkeys, terminal browser approval, physical biometrics, 1Password, host systemd isolation or the public deployment. Those require their own lanes and evidence. Fixture loopback listeners alone are not proof of operating-system-enforced network isolation.

Prepare the separate pinned Linux amd64 browser toolchain image explicitly:

anvil-serving connect qualify --prepare-container

This preparation uses the same saved settings and the local Docker engine. It may download public dependencies during the image build. Only nine named public build and dependency files enter the build context; it does not copy the checkout, home directory, Docker account configuration or deployment secrets. An owner-only receipt binds the build-input digest to the immutable image ID. Repeating the command reuses that image when its receipt and metadata match. Routine baseline runs remain download-free. Preparation alone does not run or qualify the terminal login flow.

Run the existing browser baseline in the prepared container:

anvil-serving connect qualify --lane container-baseline

This lane refuses root users and groups. It uses the recorded immutable image with downloads disabled, no external networking, no published ports, no GPUs, and no Docker socket or writable host mount inside the container. Its only host mount contains the staged public source read-only. Runtime state uses private container temporary files; CPU, memory and process counts are bounded. The runner removes its named container after success, failure or interruption and records cleanup and source/image identity with the test results. This qualifies the same two browser fixtures under container isolation. Terminal approval, virtual and physical passkeys, host service identities and production routes need separate evidence.

Run the terminal-login scenarios in the same isolated image:

anvil-serving connect qualify --lane device

This drives the actual standalone CLI, synthetic Authelia browser sign-in and approval form, then makes keyed requests through the CLI's loopback listener. Only the CLI child trusts its fixture CA; normal HTTPS hostname and certificate verification stay enabled. The lane checks absent/wrong local keys, declared models access, GET-only grant enforcement, and CLI exit/port release. Its negative scenario also checks unauthenticated approval, forged and foreign-origin CSRF, a real browser denial, and terminal cancellation before approval. A third scenario checks user disable and browser logout: an approved CLI request must return 401 without reaching the origin. The logout check uses a fresh Connect session through the existing identity-provider SSO session. Codes and credentials stay in transient fixture state, outside retained evidence.

A fourth scenario uses Chromium's virtual authenticator to enroll a discoverable passkey through the pinned provider's real registration and elevation flows. It requires user verification, clears browser cookies, signs in using the passkey, and approves an actual CLI request. The fixture's separate operator identity keeps the tested user unprivileged. Evidence labels this coverage virtual-webauthn-only: it does not verify physical presence, biometrics, Touch ID, Face ID, 1Password integration, or credential sync and backup.

The remaining passkey scenarios check missing user verification, a credential for the wrong relying party, an expired signed assertion, sequential replay, and a disabled Connect user. UV and relying-party failures are browser policy checks; replay checks rejection in the resulting authenticated session, without claiming concurrent replay resistance. A pre-enrolled spare credential restores dashboard and CLI access while user administration remains forbidden. The existing browser session and CLI key must remain usable after those forbidden requests; recovery does not grant an operator role. These are synthetic recovery and authorization checks, not validation of a production security audit log.

Device-approval expiry and single-use redemption after a lost response are covered separately by authority and CLI unit tests, using an injected clock where appropriate. The device lane does not claim those are browser end-to-end tests. Two browser stream scenarios hold SSE and WebSocket connections open across human disable and logout. They require client closure and native-handler return within one second measured from before the authority mutation, then fresh denial without another origin dispatch. Passed stream cases retain integer closure_ms in evidence and JUnit; missing or invalid measurements fail qualification. This is a synthetic fixture target, not a production latency guarantee. Two further scenarios exercise SSE and WebSocket through the actual standalone CLI after browser approval. They validate the native bearer and host, wait for an SSE marker and a complete WebSocket handshake, then disable the user or log out. Both clients and native handlers must close within the same one-second fixture bound. Fresh requests with the formerly approved local key must return 401 without another native stream, and the CLI must exit and release its port. Other authority events, authority recovery and physical passkeys require separate coverage.

Run the bounded stream-revocation subset in the same prepared container:

anvil-serving connect qualify --lane revocation

This uses the saved qualification settings, the pinned image, disabled downloads, the same configured timeout (600 seconds by default), and the existing CPU, memory and cleanup bounds. It runs exactly ten browser and CLI lifecycle cases: human disable, browser logout, synthetic session expiry, normal restart, authority reset, restore, selective key revocation and browser grant removal. The expiry fixture uses a full-precision inventory deadline across browser SSE and WebSocket plus CLI SSE and WebSocket transports, then denies fresh use of the old browser cookie and CLI key. The restart fixture stops and starts the runtime, retains a valid browser cookie and CLI credential, rejects an old revoked cookie, and checks that an interrupted POST has exactly one origin execution. The authority-reset fixture uses the supported local reset operation, creates a new epoch, denies the old cookie and CLI key, then re-enrolls a fresh connector and admits new browser and CLI credentials through the normal flows; its interrupted POST also has one origin execution. Restore uses the supported backup and fresh-destination restore APIs, denies old credentials before explicit human/API-principal reapproval and connector reenrollment, then checks fresh access while the original browser cookie and still-running old CLI remain denied. Its closure_ms measures the preceding runtime stop, not backup or restore latency. The key-revocation case keeps a second CLI credential usable while the revoked one remains denied across restart. The grant-removal case denies the removed dashboard resource across restart while fresh login to the retained resource succeeds. Each passed case retains its validated closure_ms observation. This documents the bounded fixture scope; it does not claim an executed qualification result, reboot behavior or external-supervisor recovery. Virtual and physical passkeys and component-level authority checks have separate evidence.

Prepare the pinned systemd guest image, then run service-isolation qualification:

anvil-serving connect qualify --prepare-vm
anvil-serving connect qualify --lane isolation

Both commands use the same saved qualification settings. Preparation verifies the signed image checksum and pinned image bytes, then stores an owner-only receipt. Repeating preparation verifies and reuses the cache. The isolation lane requires that prepared image and never downloads it implicitly. It also requires the pinned local component tools, cached Go modules, QEMU, KVM, xorriso and the declared firmware on Linux amd64.

The runner uses a disposable guest with two CPUs and 2 GiB of guest RAM. It has no guest network interface, GPU, shared host directory or host service socket. The payload contains a verified source snapshot and synthetic configuration; accounts, keys and certificates are generated only inside the guest. The host runner remains unprivileged. Host memory, disk, time and output checks bound the run; guest RAM is not a claim of a host cgroup memory limit. Qualification children cannot lock host memory. This lets QEMU consistently use its epoll event loop instead of partially initializing io_uring and failing on a later ring allocation. Address-space and resident-memory bounds remain enforced.

This lane checks actual installed service identities and unit bytes, Unix peer authorization, private-file and administration denials, TLS/provider discovery, certificate replacement, restart, failed-activation rollback and CLI shutdown cleanup. It retains the same four evidence files after removing guest staging. A missing final guest report has unavailable per-case outcomes and cannot pass; it is not evidence that the cases were never attempted. Full browser sign-in and physical passkeys retain their separate qualification scopes. Command availability does not establish that a guest run or production deployment has passed. An interrupted isolation run returns a nonzero, redacted result with its safe execution state and available case counts.

Validate

connect validate checks declarations and component prerequisites. An optional --service narrows the validation to the selected role. Component validation does not prove public reachability, successful login, or origin readiness.

Render

connect render reports the intended configuration generation. With --confirm, it stages generated files for inspection. It does not start services. Existing unmanaged files and changes to owned generated files are refused.

Up

connect up --service gateway previews the selected deployment. Applying it validates the candidate, activates only owned configuration and units, and starts the declared processes. Connector and client selections operate one exact named installation. A selected update cannot silently change another role's files.

For a policy change spanning roles on this host, use one explicit set such as --services gateway,connector:dashboard,client:dashboard-api in place of --service. The selected roles share one activation and rollback transaction. Artifact upgrades additionally require --upgrade: install the new artifact at a versioned path and retain the previous verified executable for rollback. Replacing an executable in place is refused. The preview includes the proposed artifact digests; ordinary identity/admin commands still refuse binary drift.

anvil-serving connect up --manifest /etc/anvil-connect/deployment.json --service gateway --dry-run
anvil-serving connect up --manifest /etc/anvil-connect/deployment.json --service gateway --confirm

The gateway requires explicit prior initialization. A connector requires its invitation to be redeemed and its fingerprint approved before it can run.

Down

connect down --service SERVICE previews stopping the selected owned services. --confirm applies the stop. Application state, authority records, and secrets remain separate from generated service configuration.

Status

Public reverse tunnels open connections on demand. Zero idle registrations are reported as idle, not a failed tunnel, and do not trigger a connector restart on an unchanged apply. Status does not replace an authenticated application request.

connect status reports bounded owned service metadata. --service narrows it. A process reported as running is not a claim that its origin is ready.

Doctor

connect doctor checks component paths and declared ownership. --service narrows the checks. Diagnostics exclude secret file contents and request bodies.

Logs

connect logs --service SERVICE --tail 100 returns bounded service event metadata. The maximum tail is 200. Credential-bearing raw process logs are not a public diagnostic contract.

Init

connect init --service gateway previews initial authority creation. Applying initialization refuses to overwrite existing authorities. Ordinary up never regenerates a missing authority silently.

For a connector, supply --service connector:ID --bundle /absolute/invitation.json. The private invitation file is produced by gateway administration. The connector generates installation keys locally; it never receives a shared private key.

Identity

connect identity --service connector:ID returns its public installation fingerprint. Verify that fingerprint independently before approving enrollment. The output excludes private key material.

Admin

connect admin --request /absolute/request.json previews a closed administrative operation against the manifest's gateway Unix socket. --confirm performs it. The native command runs under the declared service identity. Status, principal grants, API key issuance/revocation, installation invitations/approval/revocation, human grants, and authority reset use this boundary.

Issuance and invitations require --output /absolute/private/response.json. The output must not exist; its parent must be owned and mode 0700. It is reserved as a mode-0600 file before the issuing request is sent. Credentials are never printed in command output and should not be placed in the deployment manifest.

For the generic deployment example, a request file granting an API principal read access contains:

{"operation":"principal-set","principal":"sdk-reader","grants":[{"resource":"dashboard-api","methods":["GET"]}],"disabled":false}

After applying that request, a separate issuance request is:

{"operation":"api-key-issue","principal":"sdk-reader","grants":[{"resource":"dashboard-api","methods":["GET"]}],"lifetime_seconds":86400}

Apply it with --output to receive the one-time private response. Provision its secret through the caller's secret mechanism. An ordinary SDK then uses https://api.example.test/v1 and that key; a native application token stays in the connector's declared environment. Key grants are explicit and must be a subset of the principal's current grants; they are not inherited. This example grants GET only. Chat or other POST operations require an explicit matching method in both the resource declaration and caller grant.

A browser grant uses the managed issuer's exact opaque subject, replacing the placeholder before applying:

{"operation":"human-set","issuer":"https://auth.example.test","subject":"REPLACE_WITH_EXACT_OIDC_SUB","resources":["dashboard"],"disabled":false}

The subject is not the username. An invitation for the example connector names its exact resource set:

{"operation":"invite","installation":"dashboard","role":"connector","resources":["dashboard","dashboard-api"],"lifetime_seconds":300}

Save its private output, redeem it with connect init --service connector:dashboard --bundle ..., then inspect connect identity. The final approval request uses operation: approve, installation: dashboard, and the independently checked fingerprint returned by identity inspection.

Keygen

connect keygen --service client:ID --output /absolute/private/local-key previews a local SDK-forwarder key. --confirm creates it exclusively. Provision this key and the distinct remote Connect API key through the client's declared environment file. Native application credentials belong only to the origin connector.

Generated configuration rollback does not restore authority databases, sessions, invitations, or revoked credentials. Database recovery requires its separate restore workflow. Public deployment and Observatory migration still require the isolated acceptance and migration evidence described in the delivery plan; command availability alone does not qualify a production cutover.

Backup

connect backup --output /absolute/private/backup.json previews an offline gateway authority backup. Stop the gateway first; --confirm reserves an exclusive private output before opening state. The archive contains private CA keys and authorization history and is limited to 1 MiB; an oversized archive fails without truncation. Keep the returned SHA-256 separately from the backup. The file is not encrypted. Authelia storage/secrets, operator configuration, and application state have separate backup procedures.

Restore

connect restore --input /absolute/private/backup.json --destination /absolute/private/fresh-gateway --sha256 DIGEST --native-sha256 BINARY_DIGEST previews recovery using the independently retained backup digest and approved native executable digest. The latter is available in Connect validation output.

--confirm restores only into a fresh directory. Recovery retains CA identity, assigns a new authority epoch, disables recovered human/API grants, and discards historical credentials, installation bindings, invitations, and sessions. Review and reapprove grants, reenroll connectors, and issue new keys before resuming access. The command leaves configuration and running services untouched; updating the manifest's gateway state directory and activating it are separate operations. An interrupted recovery can leave an inert private directory: inspect it and retry with another fresh destination. Never replace the active authority with an old database as part of a binary or configuration rollback.

Migration

connect migration --observatory-config /absolute/observatory.json --resource ID produces a no-write plan for one browser resource and the existing Observatory canonical origin. It verifies native passthrough and path compatibility. The plan preserves native login, session, CSRF, and action authorization; it does not perform a public cutover or establish public reachability.

Edge status

connect edge-status --manifest /absolute/deployment.json --edge-config /absolute/edge-cloudflare.json compares the resources declared in the deployment manifest with the live Cloudflare state: the required CNAME record per published host, the required tunnel ingress rule per host, and the tunnel's connector status. Hosts outside the configured zone are rejected. Read-only; the API token is read from the environment reference named in the edge configuration and never appears in output.

Edge apply

connect edge-apply --manifest /absolute/deployment.json --edge-config /absolute/edge-cloudflare.json --confirm applies exactly the declared difference through the Cloudflare API: the tunnel ingress rules are merged (managed hosts first, foreign rules preserved, the catch-all kept last) and the DNS records are created or updated in place. Ingress rules for hosts that the manifest does not declare are preserved untouched. A repeated apply converges without API writes, and the apply verifies the tunnel is reporting an active connector before it reports success. Without --confirm the command prints the same plan and changes nothing.

Extend

connect extend --manifest /absolute/deployment.json --service connector:<id> --confirm widen one enrolled connector's resource set to its declaration in one managed operation: the preflight accepts purely additive resource-set changes (removals and renames are rejected with revoke-and-redeclare guidance), the new generation is activated with only gateway units restarted, the native admin revoke/invite pair runs against the running gateway, and the connector is stopped before an exact retained local identity and the invitation stage an explicit replacement. Ordinary init --bundle then redeems that staged identity, the new fingerprint is approved, and the connector starts without changing its enablement policy. The whole sequence runs inside the deployment lock with rollback of the rendered tree and gateway units. Without --confirm it prints the same plan and changes nothing. Existing principals keep their prior resource lists until connect admin human-set extends each one.

After a revoke, the operation keeps a private recovery record rather than restoring the old enrollment. A later connect extend first verifies the exact native installation status and either completes the retained forward transition or stops without further authority changes when the status has drifted. A retained staged replacement can be retried only with the same invitation and exact prior identity; invitation material is never shown or recreated from that record.