Appearance
Security model
The security model is opinionated. The forbidden items are forbidden because they were wrong-shaped attempts at this problem; the required items are what the current code enforces.
Source: WORKSTREAMS/product-launch/HIVECAST-DEVICE-ENROLLMENT-SPEC.md "Correct Security Model" section.
Forbidden (and why)
| Forbidden practice | Why it's wrong |
|---|---|
| One shared broker password for all devices | A compromised Device leaks credentials usable on every other Device. Lateral movement is a single hop |
| One raw principal account seed copied to all devices | Same problem at the NATS-account layer. The seed is the master credential; sharing it is a mass-revocation event waiting to happen |
Inline shared user:pass@host attach as the normal path | Users will paste this anywhere. Credential rotation becomes "every machine simultaneously" |
Required (and what enforces it)
| Requirement | What enforces it |
|---|---|
| Per-principal account isolation | NATS account model — one account per principal/authority root. Cross-account traffic is explicitly bridged or denied |
| Per-device credential issuance | system-auth issues a device-scoped NATS JWT to each Device on pairing. Each Device's credential is signed for that Device's scope only |
| Device-level revocation | The cloud-side link record is the source of truth; revoking it invalidates the Device's JWT (or makes the next renewal fail) |
Three identity layers (where each lives)
From the spec:
1. Principal identity
- Owned by HiveCast (the cloud)
- Created by sign-in (Google / GitHub OIDC)
- Maps to one authority root per principal (legacy email-derived; new principals get a
PRINCIPAL.<id>-shaped root)
2. Principal NATS account
- One NATS account per principal/authority root
- Managed by HiveCast
- The principal account seed stays server-side — never copied to a Device
- Devices get device-scoped credentials, not the account seed
3. Device credential
- One per attached Device/runtime
- Issued under the principal's NATS account, scoped to that Device only
- Revocable independently
- Stored locally on the Device at
<host-home>/credentials/factotum/<provider>/...(per the Factotum boundary)
hivecast-link.json carries the credentialsRef pointing at the device-scoped NATS credentials file. The principal account seed is never present in hivecast-link.json and never present on the Device.
Per-device NATS JWT (what was added in 2026-04-30)
Per HIVECAST-DEVICE-ENROLLMENT-SPEC.md "Current implementation status — 2026-04-30":
- device-code and browser-pair exchanges now return device-scoped NATS JWT credentials from
system.auth- setup exchange writes those scoped credentials to the local Device credential files when available
- the principal account seed remains server-side for NATS account provisioning
- Host Link revoke is authenticated and principal-bound
Concretely:
- After successful pairing,
system-authmints a JWT scoped to<authorityRoot>.devices.<deviceSlug>.>. - The JWT is signed by the principal's NATS account key, but the principal account seed itself is not exposed.
- The Device stores the JWT in its credentials directory, references it from
hivecast-link.json. - The Device's leaf NATS connection authenticates with the JWT.
A Device cannot impersonate another Device on the same account (its JWT subject is its own deviceSlug). Revocation is by removing the JWT from the principal's NATS account claims — done at the cloud side, no Device-side action required.
What stays local — Factotum
Per CLAUDE.md Rule 4:
system.factotumis the ONE actor that reads/writes credential files. Inference credentials NEVER leave the local machine.
This is the core privacy property: even when a Device is paired, the API keys for OpenAI / Anthropic / Codex / etc are stored only on the Device, accessible only through Factotum. The cloud never sees them. Inference calls go directly from the Device to the inference provider.
If you want HiveCast-the-cloud to never see your inference traffic, that property is what gives you that. Routing chat sessions through the cloud (sense 2 from Hosted runtimes) routes only the orchestration; the actual inference call is Device-to-provider.
Auth boundaries (per CLAUDE.md Rule 6)
| Flow | WHERE | WHY |
|---|---|---|
| Google / GitHub OIDC | HiveCast cloud | Registered redirect URIs |
| Anthropic / Codex OAuth | Local HiveCast install / Factotum | Localhost redirect, credentials stay local |
| Domain verification | HiveCast cloud | DNS TXT check |
| Inference API calls | Local runtime through Factotum-owned credentials | Tokens are local |
The local install runs an OAuth callback on 127.0.0.1 for Anthropic / Codex. The token Anthropic / Codex returns goes to Factotum, which writes it to <host-home>/credentials/factotum/<provider>/auth.json. It never touches hivecast.ai.
Identity from transport metadata, never from payload
Per CLAUDE.md Rule 5:
Transport metadata = authenticated identity. Payload = untrusted. Never trust
payload.principalId.
When an actor receives a request, the principal id (and Device id, where applicable) come from the NATS connection's auth claim, not from a payload field. A malicious caller cannot lie about its identity in the message body; it can only send what its connection's JWT authorizes.
Browser → NATS path
Per CLAUDE.md Rule 7:
Browser connects via same-origin WebSocket. Always
/nats-wson the SAME origin. Never absolute WebSocket URLs from bootstrap.
The bootstrap that the gateway returns to a fresh browser includes the relative WebSocket path (/nats-ws). The browser's NATS connection inherits the origin's authentication context (the session cookie issued by the cloud's OAuth callback or the loopback-trust on a local-client Host).
Revocation lifecycle
| Trigger | Effect |
|---|---|
| Cloud Devices page → Disconnect device | Cloud removes the Device's JWT claim. Device's next leaf-connection attempt or JWT renewal fails |
Device-side hivecast logout --revoke-cloud-link | Same: Device tells cloud to revoke. Local link file deleted |
| Principal sign-out everywhere (target state) | Future: invalidate all Device JWTs for this principal at once |
| Principal account deletion | All Devices invalidated; all Spaces released |
What's still target state
Per HIVECAST-DEVICE-ENROLLMENT-SPEC.md:
rename, richer first-class physical-device grouping, rotation, and driver / resource summaries are still future launch work
JWT rotation (renewing a Device's credentials before they expire without a full re-pair) is target state. Today, expiry is long enough that this rarely matters in practice.
See also
- Login — what authentication looks like at sense-1 (browser-side).
- Device pairing — the ceremony that issues the per-Device credential.
- CLI → hivecast login / whoami / logout.
CLAUDE.mdRules 4-7 for the authoritative architecture.
Source:
WORKSTREAMS/product-launch/HIVECAST-DEVICE-ENROLLMENT-SPEC.md"Correct Security Model"; repo-rootCLAUDE.mdRules 4-7;projects/matrix-3/packages/system-auth/src/host-auth.ts.