Skip to content

Device grants

A grant on a Device is a scope token attached to its Host Link. Scopes describe what that Device's NATS user JWT is allowed to do. This page covers the present-state implementation and the target authorization model.

Where scopes live

Source: projects/matrix-3/packages/system-auth/src/host-auth.ts:107.

ts
export interface IHostLinkRecord {
  ...
  readonly scopes: readonly string[];
  ...
}

Scopes are persisted on the Host Link record. They are set at creation time by auth.hostLink.create (or via auth.device.approve / auth.pair.approve, which forward scopes from the original start call). Scopes are immutable on a created Host Link — there is no auth.hostLink.scopes.update op. Changing a Device's scopes today requires revoking and re-pairing.

Default scopes

When a pair-flow starts via Edge, the Edge UI requests the default scope set:

  • device.runtime.read — read runtime/registry inventory on this Device.
  • device.heartbeat.write — send heartbeats to auth.hostLink.verifyHeartbeat.
  • device.credentials.refresh — refresh device-scoped credentials.

When a device-flow starts via CLI, the CLI uses the same default unless overridden with --scope.

These scope strings exist as data; they are not yet enforced. The current implementation issues a NATS user JWT with rootScopedNatsUserPermissions(authorityRoot) (see system-auth/src/index.ts:1487-1497) — full access under the principal's authority root, regardless of scopes[].

Status: scopes are recorded but not yet authorization-enforced.

What is enforced today

  • Authority-root isolation. A Device's NATS user JWT is permitted on subjects under its authorityRoot only. Cross-Space access requires explicit federation publish via system.registry.
  • Per-Device user JWT. Revoking a Host Link revokes only that Device's user JWT, by NATS user public-key revocation (recordNatsCredentialUserPublicKey).
  • Heartbeat token verification. auth.hostLink.verifyHeartbeat requires the issued heartbeat token; without it, no heartbeat can update the link.

That is the present-state security boundary. Inside the boundary, a Device's NATS JWT can do anything its authority root allows.

Target scope model

Scope strings should map to NATS subject permissions:

ScopeWhat it authorizes
device.runtime.read<authorityRoot>.system.runtimes.> subscribe
device.runtime.control<authorityRoot>.system.runtimes.> publish
device.registry.read<authorityRoot>.system.registry.> subscribe
device.registry.publish<authorityRoot>.system.registry.> publish (claim creation)
device.credentials.refresh<authorityRoot>.auth.hostLink.credentials.>
device.heartbeat.write<authorityRoot>._auth.hostLink.heartbeat
device.app.<appName>.*per-app subjects

The exact mapping is not codified. The existing rootScopedNatsUserPermissions helper builds the all-allow permission set. A scoped permission builder is target state.

Listing a Device's scopes

bash
matrix invoke system.auth auth.hostLink.get \
  '{"hostLinkId":"<id>"}'
# Look at hostLink.scopes

Or list all Host Links for a principal:

bash
matrix invoke system.auth auth.hostLink.list \
  '{"principalId":"p_xxx"}'
# Each hostLink row includes scopes.

Granting on existing Devices

Until scope updates are implemented, the procedure is:

  1. Identify the Device by hostLinkId.
  2. Revoke it (auth.hostLink.revoke).
  3. The Device retries enrollment with the new scope set.

This is disruptive. Target state is auth.hostLink.scopes.update accepting an additive scope list and rotating the user JWT under the new permissions.

Org-level grants (target)

Per Organizations, the target model has org-scoped Devices. A Device bonded to an org Space inherits scopes from the org's defaults, with optional per-Device overrides. None of that exists; orgs themselves are target state.

Why this matters for security audits

A reviewer asking "what can this Device do?" should be able to read IHostLinkRecord.scopes and trust the list. Today the answer is "everything its authorityRoot allows." This is acceptable for the launch model (single-user-per-Device, user fully trusts their own laptop) but does not support delegation — a contractor's laptop should have read-only access; today the only safe path is "don't pair the contractor's laptop."

See also

Source: projects/matrix-3/packages/system-auth/src/host-auth.ts:107. projects/matrix-3/packages/system-auth/src/index.ts:1487-1497 for the (current all-allow) NATS permission builder.