Skip to content

Client Bootstrap Contracts

MAC ships the complete SSH-first login orchestration plus its three lower-level security contracts:

  • mac admin fleet ssh-spec resolves one explicit, secret-free SSH route from ~/.mac/fleets.yaml.
  • mac admin client enroll|renew|revoke|list manages independently revocable, scoped client principals on the hub.
  • mac admin client profile ... atomically installs and selects a portable local profile whose bearer token is kept in a separate mode-0600 credential record.
  • mac admin login, mac admin login status, mac admin login renew, and mac admin logout --revoke compose those primitives into a verified, recoverable client lifecycle.
  • mac admin login --local-console provides a separate hub-local enrollment path through the running API service without exposing the shared admin token.

Managed Login

From a shell on the hub, use the kernel-authenticated local console instead of SSH:

mac admin login --local-console --profile local --client-id hub-console
mac admin login renew --local-console --profile local

The client sends one bounded JSON request to MAC_LOCAL_CONSOLE_SOCKET. The packaged systemd configuration sets /run/mac/local-console.sock; an unconfigured user service defaults to ~/.mac/local-console.sock. The API service authorizes the Unix-socket peer UID from kernel credentials; loopback TCP is not an enrollment authority. By default only the service UID and root can connect. MAC_LOCAL_CONSOLE_GROUP may grant an explicit local group access, with the socket directory and socket set to 0750 and 0660 for that same group. Without a group they are 0700 and 0600.

The direct profile URL is selected from MAC_API_URL, MAC_URL, or MAC_HUB_URL, then falls back to http://127.0.0.1:$MAC_PORT. The issued bearer is validated against that API before the profile is installed or activated. Validation failure triggers best-effort revocation and leaves no local profile. Ordinary enrollment is limited to read,write,dispatch. A broader scope requires explicit --allow-elevated acknowledgement and the peer must be root or the OS account running the API service. This matches the authority that account already has when it invokes enrollment through SSH; supplementary-group users remain limited to ordinary scopes.

Local-console renewal rotates through the same kernel-authenticated socket and validates the replacement bearer before atomically replacing the profile credential. The old bearer is invalid as soon as the service rotates it. If replacement validation fails, the service revokes the new bearer and keeps the local profile file unchanged, while reporting explicitly that its retained old credential is no longer valid. Renewing a credential whose scopes exceed read,write,dispatch still requires the service owner or root plus --allow-elevated.

For an explicit hub route:

mac admin login --ssh mac@hub.internal \
  --identity-file ~/.ssh/mac-production \
  --known-hosts-file ~/.ssh/mac-production-known-hosts \
  --fleet production --profile production --client-id my-laptop

Or consume the canonical route in ~/.mac/fleets.yaml:

mac admin login --fleet production --profile production --client-id my-laptop

Identity and host trust are validated when supplied and otherwise fall back to OpenSSH's own resolution, so mac admin login --ssh <host> behaves like ssh <host>:

  • Identity — an explicit --identity-file (or a fleet identity_file) is validated (must exist, chmod 600) and pinned with IdentitiesOnly. When none is given, ssh selects its default identities and the agent.
  • Host trust — an explicit --known-hosts-file, --host-ca, or --host-key-fingerprint pins the host under strict checking. A directly reachable host pinned by fingerprint is scanned with ssh-keyscan, and only the matching key is retained in a private profile-owned known-hosts file (fingerprint discovery is refused for a ProxyJump route, since an unauthenticated scan cannot authenticate both hops — provide a verified known-hosts file instead). When no trust material is given, login verifies against the operator's default ~/.ssh/known_hosts using accept-new (trust-on-first-use), matching interactive ssh rather than failing.

For a reproducible, portable client profile — one that does not depend on the enrolling machine's ambient ssh state — supply both an explicit identity and explicit host trust (or configure them on the fleet's hub agent). Exported profiles record exactly what was resolved.

The command reserves a free loopback port and starts SSH with -F /dev/null, BatchMode=yes, ExitOnForwardFailure=yes, strict host checking, server-alive probes, and the explicit route. It invokes scoped enrollment in a second SSH session, running the hub's mac executable. Because a non-interactive SSH command shell does not source the operator's login profile, the remote mac path is discovered automatically (well-known install locations plus the shell's own command -v); pass --remote-mac <abs path> only to override that. It then validates GET /tasks/stats with the returned bearer through the tunnel, writes secret-free session state, and atomically installs the profile only after validation succeeds. Any failure after issuance attempts remote revocation and removes transient state without printing the bearer.

After login, operator commands omit --db and resolve the installed profile as the one hub authority. MAC no longer creates a client-side database implicitly. A database reached through --db is neither a cache nor an offline queue, and MAC performs no implicit task reconciliation between databases. If one already contains tasks, task-producing commands refuse to add more unless --local-authority explicitly declares that it is the database used by a standalone API, dispatcher, and worker set.

There is no transfer path from such a database into a hub. Work the fleet must run is created against the hub. The former mac admin migrate local-ledger command and its login-time local_ledger notice were retired with the SQLite backend.

The same rule applies to deployed fleet spokes. They do not run a local control-plane service and their generated environment contains no database setting. Workers, fleet-context refresh, and Hermes identity registration all use the hub API.

The managed SSH PID is recorded under $MAC_HOME/sessions; bearer material is not. Every profile-backed CLI command checks the session. A dead tunnel is restarted from the pinned profile and its bearer is validated before the requested API call proceeds. mac admin login status is read-only and reports a dead, unreachable, or rejected session without exposing the token.

mac admin login status --profile production
mac admin login renew --profile production
mac admin logout --profile production --revoke

Renewal rotates rather than extends the bearer. Revoking logout performs the hub revocation first; if SSH revocation fails, local credential state is kept so the operator can retry. Plain mac admin logout is deliberately local-only and leaves the remote principal active.

Trust Boundary

Enrollment is a hub-local operation invoked through either an SSH session that already authenticated the human or automation identity, or the API service's Unix socket after kernel peer-credential authorization. It is not an HTTP endpoint, does not trust loopback TCP as an enrollment authority, and does not accept or copy the shared administrator bearer as a bootstrap credential. Only the API service writes the principal registry on the local-console path.

The hub registry defaults to $MAC_HOME/client-principals.json and can be overridden with MAC_CLIENT_PRINCIPALS_FILE. It contains SHA-256 token hashes, scope metadata, expiry, and revocation state. Live bearer material is returned once in the enrollment or renewal manifest. The adjacent client-principals.audit.jsonl records issuance, renewal, rotation, and revocation without recording the token or its full hash.

The API hot-reloads this registry. Issuance and revocation take effect without restarting the hub. If a previously active registry becomes unreadable or has no active principals, authenticated routes fail closed; the static MAC_API_TOKEN remains the recovery authority when configured.

Default client scopes are read, write, and dispatch. secret, deploy, or admin require the explicit --allow-elevated acknowledgement. Two client IDs always receive different credentials, and renewing one immediately invalidates its prior token.

Explicit SSH Route

Put route references, not private key bytes, in the home-scoped fleet registry:

fleets:
  production:
    hub_agent: hub
    control_port: 8789
    defaults:
      ssh_jump: ops@bastion.example:2222
      identity_file: ~/.ssh/mac-production
      ssh_known_hosts_file: ~/.ssh/mac-production-known-hosts
      ssh_host_key_policy: strict
    agents:
      - name: hub
        target: mac@hub.internal
        ssh_port: 22
        os: linux

Per-agent values override defaults. Supported route fields are target/user, port, ssh_jump, identity_file or identity_ref, ssh_known_hosts_file, ssh_host_key_fingerprint, ssh_host_ca, and ssh_host_key_policy (strict, accept-new, or the deliberately explicit insecure). The legacy ssh_strict_host_key_checking: false maps to accept-new, not disabled verification.

ssh_host_ca is a client-local known-hosts-format file containing @cert-authority entries. It is passed to OpenSSH as UserKnownHostsFile; it must never contain a host CA private key.

Inspect the exact route without exposing key material:

mac admin fleet ssh-spec --fleet production --agent hub --portable --json

--portable rejects a route that depends on an ambient identity or has no explicit host-key source. Generated SSH and SCP argv include -F /dev/null, so wildcard entries in a developer's ~/.ssh/config cannot silently change the route. Fleet token recovery, deploy, soul snapshot, agent migration, and the Electron bridge consume this same resolver.

Manual SSH Enrollment (Recovery)

For a hub whose API listens only on 127.0.0.1:8789, keep a verified tunnel open in one terminal:

ssh -N -L 8789:127.0.0.1:8789 \
  -i ~/.ssh/mac-production \
  -o UserKnownHostsFile=~/.ssh/mac-production-known-hosts \
  -o StrictHostKeyChecking=yes \
  -J ops@bastion.example:2222 \
  mac@hub.internal

In another terminal, stream the one-time manifest directly into the secure profile installer so the token is not left in a temporary file:

ssh -T mac@hub.internal \
  'mac --json admin client enroll my-laptop \
    --name "My laptop" \
    --fleet production \
    --profile production \
    --api-url http://127.0.0.1:8789 \
    --scopes read,write,dispatch' \
  | mac admin client profile install -

mac --profile production diagnostics
mac --profile production task stats
mac --profile production agent list

Use the same explicit SSH route for both commands in real deployments. The abbreviated second command above assumes the route is already pinned in an operator-controlled wrapper or uses the full options from the first command.

The profile is stored as ~/.mac/clients/<profile>.yaml. It contains no bearer value, MAC_SECRET_KEY, provider credential, Git deploy credential, private key, SQLite database, or source-checkout path. Its credential reference points to ~/.mac/credentials/clients/*.token, which is mode 0600; both parent trees are mode 0700. Normal profile list and profile show output never returns the token.

Renewal And Revocation

Renewal rotates the credential rather than extending the old bearer:

ssh -T mac@hub.internal \
  'mac --json admin client renew my-laptop' \
  | mac admin client profile install - --profile production

Revoke on the hub, then remove local state:

ssh -T mac@hub.internal 'mac admin client revoke my-laptop'
mac admin client profile remove production

Revoking one principal does not affect any other client or the recovery admin token.

Legacy Migration

The historical shared MAC_API_TOKEN has administrator authority and is a recovery mechanism, not new-client enrollment.

The bounded migration command can import an existing fleet route and token into the separated profile layout, but it refuses unless the administrator authority is acknowledged:

mac admin client profile migrate-legacy \
  --fleet production \
  --allow-legacy-admin-token

The command is idempotent and creates a mode-0600 provenance backup on the first import. The hub now supports scoped client principals, so replace the migrated admin profile with a scoped SSH enrollment immediately.

Failure Rules

  • Duplicate enrollment refuses by default; use client renew or the explicit client enroll --rotate behavior.
  • Registry and credential files with group/world permissions are ignored or rejected.
  • Unknown scopes and unacknowledged elevated scopes are rejected.
  • Profile installation rejects unknown manifest fields, credential-bearing URLs, malformed tokens, and strict SSH profiles without pinned host identity.
  • Credential writes commit before profile references. Interrupted installation therefore leaves the old complete profile active or an unreferenced new credential, never a profile pointing at a missing token.
  • Managed login refuses an occupied requested port before enrollment. A stale session is restarted only when the recorded PID is still identifiable as the expected SSH forward; unrelated processes are never killed.
  • The token is never placed in SSH argv, local session state, normal/JSON output, or error text. It exists in memory until API validation and then only in the mode-0600 credential record.