Skip to content

Requirements

This is the sector-standard requirements checklist that the LTG implements. Every requirement has an ID and a one-line acceptance test. Requirements are referenced from the RFC, the security rules, and the roadmap.

DR — Device Registration

IDRequirementAcceptance test
DR-1A device row MUST be created via a SECURITY DEFINER RPC; direct INSERT is denied.INSERT INTO devices.registered_devices ... from authenticated is rejected.
DR-2A new device starts in trust_level='pending'.Newly registered device has pending.
DR-3A device row MUST have a non-null lenser_id matching the calling Lenser.Cross-Lenser registration is rejected.
DR-4A device's name is unique per Lenser.Duplicate name returns device_name_taken.
DR-5A device's public_key is set during the two-step approve flow.pending rows MAY have NULL public key; approved+ rows MUST have non-null public key.

DA — Device Approval (two-step)

IDRequirementAcceptance test
DA-1The pending device MUST post a signed challenge before the owner can approve.Approving a device with no challenge returns awaiting_device_challenge.
DA-2The challenge envelope MUST verify against the device's submitted public_key.Forged signature returns signature_mismatch.
DA-3Only the owning Lenser may approve.Cross-Lenser approve returns not_owner.
DA-4Approval flips trust_level: pending → approved and persists the public key.Verified by lf gateway devices.
DA-5An approval older than 24 h without a heartbeat is allowed but the device shows gateway_status='disconnected'.Cron reconciliation.

DH — Device Heartbeats

IDRequirementAcceptance test
DH-1Heartbeats MUST be signed envelopes.Unsigned heartbeats are rejected.
DH-2Heartbeats update last_heartbeat_at and daemon_version.Verified after fn_device_heartbeat.
DH-3Missing heartbeats >24 h transition the device to offline.Cron sets trust_level='offline' (advisory).
DH-4Repeated signature failures within 1 h transition the device to unhealthy.Cron sets trust_level='unhealthy'.
DH-5Heartbeats MUST be idempotent (replays of in-window envelopes are rejected).nonce_cache rejects replay.

RH — Lenser Heartbeats

IDRequirementAcceptance test
RH-1A lenser MUST be bound to exactly one device via execution.runner_device_bindings(status='active').Verified by fn_runner_list_with_devices.
RH-2A lenser heartbeat is reported as part of the device heartbeat envelope (body.runners[]).Verified at write time.
RH-3A lenser whose bound device is revoked/blocked/unhealthy MUST refuse new executions.Daemon refuses; server RPC also refuses.
RH-4A lenser-bound execution that lacks a corresponding heartbeat in the last 5 minutes degrades trust.agent_verified ceiling for that submission.

EA — Execution Attestations

IDRequirementAcceptance test
EA-1An attestation MUST reference an existing execution.runs.id owned by the calling Lenser.Cross-owner reference rejected.
EA-2An attestation MUST be signed by the device's Ed25519 key.Forged signature rejected.
EA-3The server SHALL set signed, gateway_verified, device_trusted, policy_passed after server-side derivation.Client-supplied values are ignored.
EA-4The attestation row is append-only.UPDATE/DELETE blocked by public.fn_deny_mutation triggers.
EA-5A submission's trust level is recomputed on attestation insert and surfaced via fn_get_submission_trust.Verified end-to-end.

SC — Sync Conflict Handling

IDRequirementAcceptance test
SC-1Each conflict-aware object class declares a merge function.Per-class entry in libs/infra/gateway/src/lib/object-classes.ts.
SC-2Merge MUST be deterministic given the same inputs.Property test in libs/infra/gateway.
SC-3Auto-merge that cannot resolve creates a conflict row visible to lf gateway sync status --conflicts.UI surfaces.
SC-4A conflict resolution write goes through fn_sync_resolve_conflict and is audit-logged.Audit chain entry.
SC-5Local-only classes MUST NEVER appear in the outbox.Daemon rejects local_only_class.

SB — Sync Bidirectional Cloud

IDRequirementAcceptance test
SB-1Pull MUST advance per-(device, class) watermark atomically with the row return.Replay returns the same set.
SB-2Push MUST verify envelope before applying any entry.Forged envelope returns signature_mismatch.
SB-3Push and pull are scoped to the calling Lenser by RLS-backed DEFINER RPCs.Cross-Lenser reads/writes rejected.
SB-4Cloud-authoritative classes are read-only on edges.Edge push attempt rejected with cloud_authoritative.

TS — Tailscale / Private-Network Transport

IDRequirementAcceptance test
TS-1The daemon MUST NOT bind a Tailscale interface unless --tailscale is supplied.Default boot binds 127.0.0.1 only.
TS-2Tailscale presence MUST be ignored for authentication.A request from a Tailscale peer without a valid signed envelope is rejected.
TS-3The daemon detects Tailscale interfaces by CIDR (100.64.0.0/10) and matching interface flags.Detector unit-tested.
TS-4lf gateway doctor --check transport reports the active bind set.Visible in CI smoke output.

LH — Localhost Exposure Rules

IDRequirementAcceptance test
LH-1The daemon binds 127.0.0.1 only by default.Boot logs the bind list.
LH-2Adding 0.0.0.0 is forbidden in v1.Daemon refuses with public_bind_forbidden.
LH-3Daemon's bind port is configurable via --port and defaults to 38080.Port collision logs and exits non-zero.
LH-4mDNS advertisement is opt-in.Off by default.

CO — Config Ownership

IDRequirementAcceptance test
CO-1.lenserfight.json MUST NOT contain secrets.saveConfig strips secrets; doctor warns if any leak.
CO-2~/.lenserfight/lenserfight.json MUST hold tokens only, never signing keys.Schema-tested.
CO-3~/.lenserfight/gateway/ is daemon-private state, mode 0700 (dir) and 0600 (files).Filesystem check.
CO-4OS keychain holds signing keys; access mediated by libs/utils/keychain.Verified at boot.
CO-5Environment variables hold bootstrap secrets only.Doctor confirms.

SH — Secret Handling

IDRequirementAcceptance test
SH-1Private keys NEVER leave the OS keychain (except export-public for public key).Audit.
SH-2BYOK keys NEVER appear in DB or .lenserfight.json.Existing rule in byok-key-resolver.
SH-3Daemon redacts envelopes from logs (truncate to 8 base64 chars).Log inspection in tests.
SH-4service_role SHALL NOT appear in daemon or CLI runtime config.Daemon refuses to start with service_role_in_daemon.

DG — Doctor Diagnostics

IDRequirementAcceptance test
DG-1lf gateway doctor returns non-zero on any failed check.CI smoke.
DG-2Each check has an explicit machine-parseable code.JSON output mode.
DG-3Doctor MUST not require the daemon to be running for clock, keychain, identity checks.Verified.
DG-4Doctor MUST run daemon and sync checks against the daemon when present.Verified.
DG-5Doctor SHOULD finish in < 5 s on a healthy machine.Soft target.

AU — Auditability

IDRequirementAcceptance test
AU-1Every device trust transition appends to audit.hash_chains with chain_kind='gateway'.Trigger-tested.
AU-2Every signed RPC writes a chain entry on success.Verified.
AU-3Audit entries are append-only.UPDATE/DELETE blocked.
AU-4Audit chain is verifiable via audit.fn_chain_verify(p_lenser_id UUID, p_kind TEXT).Returns OK / first-bad-index.

PE — Policy Enforcement

IDRequirementAcceptance test
PE-1agents.fn_evaluate_pre_run_policy is called before any sensitive run.Server side.
PE-2Daemon caches policy verdicts ≤ 60 s.Verified.
PE-3Policy verdict deny short-circuits the run; no attestation is recorded.Verified.

KS — Kill-Switch Integration

IDRequirementAcceptance test
KS-1global_kill_switch=true blocks daemon startup.Verified.
KS-2runner_paused=true blocks new attestations for the workspace.Verified.
KS-3Kill switch state is polled every 10 s by the daemon.Verified.
KS-4Kill switch transitions are audit-logged.Verified.

RP — Replay Protection

IDRequirementAcceptance test
RP-1Envelope iat MUST be within ±300 s of server clock.Out-of-window rejected.
RP-2Envelope nonce MUST be unique within 600 s.Replay rejected.
RP-3Nonce cache is cleaned periodically.Cron.

WT — Workflow Trust

IDRequirementAcceptance test
WT-1An attestation reaches fully_trusted only with non-null workflow_hash AND lens_hash.Verified.
WT-2Workflow definitions referenced by an in-flight attestation MUST be pinned to a lenses.versions.id.Pin enforced.
WT-3Tool invocations contribute to policy_passed evaluation.Verified.

AC — Anti-Cheat Execution Validation

IDRequirementAcceptance test
AC-1Clients MAY NOT set gateway_verified = true.RPC ignores client value.
AC-2Clients MAY NOT set device_trusted = true.RPC ignores client value.
AC-3Clients MAY NOT set policy_passed = true.RPC ignores client value.
AC-4XP-minting paths require server-derived gateway_verified=true.Trigger condition.
AC-5Reputation pipelines consume server-derived trust levels only.Verified.

NR — Naming Reconciliation

IDRequirementAcceptance test
NR-1The canonical workspace pause column name is runner_paused. Code that uses agent_paused MUST be renamed (or aliased with a deprecation comment) in Phase A.Single column name across docs and code.
NR-2The CLI flag and docs use lenser, never agent, for execution-pause concepts.Doc + code grep returns no agent_paused matches by end of Phase A.
NR-3Where the legacy term must be retained (DB column already named agent_paused), the migration creates a generated column or view alias to expose the new name without breaking callers.Migration test.