raf: sync docs to code + relative cross-reference links
Handler/migration counts, calculateBackoff -> calculateDelay, machine-id binding (cross-platform machineid + fallbacks, not hostname), last-reviewed dates, and made [[cross-references]] relative so they resolve.
This commit is contained in:
parent
1d84fd46a7
commit
d9ba008f67
13 changed files with 108 additions and 109 deletions
|
|
@ -12,7 +12,7 @@ server/
|
|||
│ └── main.go # Entry point, route registration, service initialization
|
||||
├── internal/
|
||||
│ ├── api/
|
||||
│ │ ├── handlers/ # HTTP handlers (30+ files)
|
||||
│ │ ├── handlers/ # HTTP handlers (50+ files)
|
||||
│ │ │ ├── agents.go # Agent CRUD, commands
|
||||
│ │ │ ├── auth.go # JWT management
|
||||
│ │ │ ├── agent_updates.go # Update approval
|
||||
|
|
@ -26,7 +26,7 @@ server/
|
|||
│ │ └── require_admin.go # Admin checks
|
||||
│ ├── database/
|
||||
│ │ ├── db.go # Connection + migration runner
|
||||
│ │ ├── migrations/ # Numbered SQL migrations (001–055)
|
||||
│ │ ├── migrations/ # Numbered SQL migrations (001–058)
|
||||
│ │ └── queries/ # SQL queries (sqlx)
|
||||
│ ├── models/ # Go structs for all entities
|
||||
│ ├── scheduler/ # Background job scheduling
|
||||
|
|
@ -64,7 +64,7 @@ server/
|
|||
- Connection pooling: 25 max open, 5 idle
|
||||
- Queries: sqlx for parameterized queries
|
||||
|
||||
**Migrations:** 55 numbered SQL migrations (001–055), idempotent DDL. Notable:
|
||||
**Migrations:** numbered SQL migrations (001–058, with lettered sub-steps 009b, 012b, 023a, 025b), idempotent DDL. Notable:
|
||||
- 042: `capability_tokens` table (supply chain gate)
|
||||
- 045: refresh-token rotation lineage (`family_id`, `consumed_at`, `superseded_by`)
|
||||
- 047: package state machine enforcement (`PackageStatus` CHECK constraint)
|
||||
|
|
@ -72,7 +72,7 @@ server/
|
|||
|
||||
**Cross-references:**
|
||||
- [deployment/01-docker-stack](../deployment/01-docker-stack.md) (database configuration)
|
||||
- [testing/01-test-pyramid](testing/01-test-pyramid.md) (migration test coverage)
|
||||
- [testing/01-test-pyramid](../testing/01-test-pyramid.md) (migration test coverage)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -98,8 +98,8 @@ func SignCommand(cmd *Command, privateKey *ed25519.PrivateKey) (*Signature, erro
|
|||
```
|
||||
|
||||
**Cross-references:**
|
||||
- [verification/01-signing-pipeline](verification/01-signing-pipeline.md) (signing service)
|
||||
- [verification/02-agent-verification](verification/02-agent-verification.md) (verification flow)
|
||||
- [verification/01-signing-pipeline](../verification/01-signing-pipeline.md) (signing service)
|
||||
- [verification/02-agent-verification](../verification/02-agent-verification.md) (verification flow)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -130,7 +130,7 @@ func DownloadAgent(w http.ResponseWriter, r *http.Request) {
|
|||
```
|
||||
|
||||
**Cross-references:**
|
||||
- [flows/03-agent-upgrade](flows/03-agent-upgrade.md) (agent download flow)
|
||||
- [flows/03-agent-upgrade](../flows/03-agent-upgrade.md) (agent download flow)
|
||||
- [security/04-machine-binding](../security/04-machine-binding.md) (download authentication)
|
||||
|
||||
**Known issues:**
|
||||
|
|
@ -151,9 +151,9 @@ func DownloadAgent(w http.ResponseWriter, r *http.Request) {
|
|||
- Checks maintenance windows before creating install commands
|
||||
|
||||
**Cross-references:**
|
||||
- [flows/05-capability-advertisement](flows/05-capability-advertisement.md) (capability advertisement integration)
|
||||
- [components/02-agent](components/02-agent.md) (agent polling loop)
|
||||
- [core/02-architecture-decisions](core/02-architecture-decisions.md) §12 (scheduler job eviction)
|
||||
- [flows/05-capability-advertisement](../flows/05-capability-advertisement.md) (capability advertisement integration)
|
||||
- [components/02-agent](02-agent.md) (agent polling loop)
|
||||
- [core/02-architecture-decisions](../core/02-architecture-decisions.md) §12 (scheduler job eviction)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -179,10 +179,10 @@ func DownloadAgent(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
- **HTTP API** → [security/01-trust-boundaries](../security/01-trust-boundaries.md)
|
||||
- **Database** → [deployment/01-docker-stack](../deployment/01-docker-stack.md)
|
||||
- **Command signing** → [verification/01-signing-pipeline](verification/01-signing-pipeline.md)
|
||||
- **Binary distribution** → [flows/03-agent-upgrade](flows/03-agent-upgrade.md)
|
||||
- **Scheduler** → [flows/05-capability-advertisement](flows/05-capability-advertisement.md)
|
||||
- **Command signing** → [verification/01-signing-pipeline](../verification/01-signing-pipeline.md)
|
||||
- **Binary distribution** → [flows/03-agent-upgrade](../flows/03-agent-upgrade.md)
|
||||
- **Scheduler** → [flows/05-capability-advertisement](../flows/05-capability-advertisement.md)
|
||||
|
||||
---
|
||||
|
||||
*Last reviewed: 2026-06-11*
|
||||
*Last reviewed: 2026-06-14*
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
## Doctrine
|
||||
|
||||
The agent does not track lifecycle states, make policy decisions, or hold install privileges on gated ecosystems. The server owns every state transition. The agent's only autonomous decisions are: verify this signature, check this nonce, reject this replay. See [core/02-architecture-decisions](core/02-architecture-decisions.md).
|
||||
The agent does not track lifecycle states, make policy decisions, or hold install privileges on gated ecosystems. The server owns every state transition. The agent's only autonomous decisions are: verify this signature, check this nonce, reject this replay. See [core/02-architecture-decisions](../core/02-architecture-decisions.md).
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -73,9 +73,9 @@ agent/
|
|||
Discovery (scan, dry-run, hash-resolve) always runs unprivileged through `DiscoveryRunner`. Sudoers grants only discovery commands plus the single helper invocation line — zero sudo otherwise.
|
||||
|
||||
**Cross-references:**
|
||||
- [security/05-supply-chain-gate](security/05-supply-chain-gate.md) (token contract)
|
||||
- [flows/02-command-execution](flows/02-command-execution.md) (command path)
|
||||
- [flows/07-process-scan](flows/07-process-scan.md) (process explorer flow)
|
||||
- [security/05-supply-chain-gate](../security/05-supply-chain-gate.md) (token contract)
|
||||
- [flows/02-command-execution](../flows/02-command-execution.md) (command path)
|
||||
- [flows/07-process-scan](../flows/07-process-scan.md) (process explorer flow)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -87,18 +87,18 @@ Discovery (scan, dry-run, hash-resolve) always runs unprivileged through `Discov
|
|||
- Signing-required is doctrine, not config. There is no skip path.
|
||||
|
||||
**Cross-references:**
|
||||
- [verification/02-agent-verification](verification/02-agent-verification.md) (full pipeline)
|
||||
- [verification/04-replay-protection](verification/04-replay-protection.md)
|
||||
- [verification/02-agent-verification](../verification/02-agent-verification.md) (full pipeline)
|
||||
- [verification/04-replay-protection](../verification/04-replay-protection.md)
|
||||
|
||||
---
|
||||
|
||||
## Resilience Machinery
|
||||
|
||||
- **Instance lock** — `Global\RedFlagAgent_v1` mutex / flock; prevents two agents racing one `config.json` and burning refresh-token rotations ([security/03-refresh-tokens](security/03-refresh-tokens.md))
|
||||
- **Instance lock** — `Global\RedFlagAgent_v1` mutex / flock; prevents two agents racing one `config.json` and burning refresh-token rotations ([security/03-refresh-tokens](../security/03-refresh-tokens.md))
|
||||
- **Circuit breakers** — fragile scanners (notably WUA) trip open instead of hammering; health reported to server
|
||||
- **TeeLogger** — every loop event goes to both structured local log and server-bound buffer; tracker save failures tee inward (ETHOS #1)
|
||||
- **At-least-once acks** — `acknowledgment/tracker.go` persists until the server confirms result-recorded
|
||||
|
||||
---
|
||||
|
||||
*Last reviewed: 2026-06-11*
|
||||
*Last reviewed: 2026-06-14*
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
React 18 + TypeScript 5 + Vite + Tailwind 3, react-router 6, react-hot-toast. No state framework beyond a small store (`lib/store.ts`); server is the source of truth, the UI polls.
|
||||
|
||||
**Build embedding:** the production bundle is staged into `server/internal/webui/dist` before the server compiles — that directory is gitignored, so a bare `go build` embeds an *empty* UI. The release pipeline stages it; local dev runs Vite separately. See [deployment/01-docker-stack](deployment/01-docker-stack.md).
|
||||
**Build embedding:** the production bundle is staged into `server/internal/webui/dist` before the server compiles — that directory is gitignored, so a bare `go build` embeds an *empty* UI. The release pipeline stages it; local dev runs Vite separately. See [deployment/01-docker-stack](../deployment/01-docker-stack.md).
|
||||
|
||||
**Aesthetic:** hand-crafted 90's Novell look. This is deliberate and load-bearing for the project's identity — no modern flat-design rewrites.
|
||||
|
||||
|
|
@ -55,16 +55,16 @@ web/src/
|
|||
- **One way to filter.** Filter state syncs to URL via `useFilterUrl`; free-text search uses a local `useState` + `useDebounce` pair (instant feedback in the input, debounced value for API calls). Compose both into a `FilterBar`. No ad-hoc `useState` chains for filter state.
|
||||
- **Polling intervals** come from `POLL.*` in `lib/polling.ts` — no hardcoded milliseconds in components.
|
||||
- **Render the divergence, not the union** (framework §11.8): when agent-reported and server-expected state differ, the UI shows the difference, it does not paper over it.
|
||||
- All routes sit behind `WebAuthMiddleware` (admin routes additionally behind `AdminRoleMiddleware`) — see [security/01-trust-boundaries](security/01-trust-boundaries.md).
|
||||
- All routes sit behind `WebAuthMiddleware` (admin routes additionally behind `AdminRoleMiddleware`) — see [security/01-trust-boundaries](../security/01-trust-boundaries.md).
|
||||
|
||||
---
|
||||
|
||||
## Honest Gaps
|
||||
|
||||
- No automated web tests ([testing/01-test-pyramid](testing/01-test-pyramid.md))
|
||||
- No automated web tests ([testing/01-test-pyramid](../testing/01-test-pyramid.md))
|
||||
- Mobile layout usable, not optimized
|
||||
- Several UI coverage gaps tracked as `UI-*` tasks (not architecture — task tier)
|
||||
|
||||
---
|
||||
|
||||
*Last reviewed: 2026-06-11*
|
||||
*Last reviewed: 2026-06-14*
|
||||
|
|
|
|||
|
|
@ -8,13 +8,13 @@
|
|||
|
||||
The helper (`helper/src/main.rs`, a single ~2,000-line binary) is the only thing on a gated host allowed to mutate packages, and it earns that privilege by being structurally incapable of being talked into anything. It has no network stack in play, reads its trust inputs from root-owned pinned files, and performs exactly one operation per invocation — the one described by a validly signed capability token. Everything else is a typed denial.
|
||||
|
||||
Deny-by-default is the architecture, not a configuration: every failure path returns a `Denial` with a distinct exit code and a `log_security` entry. There is no flag that weakens verification. See [security/05-supply-chain-gate](security/05-supply-chain-gate.md) for the token contract this enforces.
|
||||
Deny-by-default is the architecture, not a configuration: every failure path returns a `Denial` with a distinct exit code and a `log_security` entry. There is no flag that weakens verification. See [security/05-supply-chain-gate](../security/05-supply-chain-gate.md) for the token contract this enforces.
|
||||
|
||||
---
|
||||
|
||||
## Invocation
|
||||
|
||||
The agent invokes it via `sudo systemd-run --pipe --property=ProtectSystem=no` — a transient unit with full filesystem access, separate from the agent's own locked-down unit. The token arrives on stdin (or a file path for self-update flows). The agent holds zero install sudo; the sudoers file grants discovery commands plus this one invocation line. See [components/02-agent](components/02-agent.md).
|
||||
The agent invokes it via `sudo systemd-run --pipe --property=ProtectSystem=no` — a transient unit with full filesystem access, separate from the agent's own locked-down unit. The token arrives on stdin (or a file path for self-update flows). The agent holds zero install sudo; the sudoers file grants discovery commands plus this one invocation line. See [components/02-agent](02-agent.md).
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -24,7 +24,7 @@ The agent invokes it via `sudo systemd-run --pipe --property=ProtectSystem=no`
|
|||
|
||||
1. **Trust-input self-validation (SEC-021)** — `validate_trusted_path`: every file the helper relies on (keyring dir, agent-id file, state) must be root-owned, not a symlink, and not group/other-writable. The helper defends its own inputs instead of trusting that the installer set permissions correctly.
|
||||
2. **Keyring load** — pinned Ed25519 public keys from `/etc/redflag/trusted-keys`. Verify keys, not servers (load-bearing constraint #4).
|
||||
3. **Closure hash** — `closure_hash()` recomputes the canonical hash over the token's resolved closure. This must be **byte-identical** to the Go implementation; cross-language tests pin the contract ([testing/01-test-pyramid](testing/01-test-pyramid.md)).
|
||||
3. **Closure hash** — `closure_hash()` recomputes the canonical hash over the token's resolved closure. This must be **byte-identical** to the Go implementation; cross-language tests pin the contract ([testing/01-test-pyramid](../testing/01-test-pyramid.md)).
|
||||
4. **Signature** — `verify_signature()` checks the token's Ed25519 signature over the signed message (which embeds the closure hash) against the keyring, by `key_id`.
|
||||
5. **Artifact hashes** — `verify_artifacts()` SHA-256s every artifact the token authorizes. A mismatch anywhere is a denial.
|
||||
6. **Replay check** — `replay_check_and_record()`: token IDs are recorded in local state; a token executes once.
|
||||
|
|
@ -35,13 +35,13 @@ The agent invokes it via `sudo systemd-run --pipe --property=ProtectSystem=no`
|
|||
|
||||
## Binary Self-Update Path
|
||||
|
||||
Agent, helper, and desktop binaries update through the same gate as packages: `stage_and_verify_binary` (hash check before anything moves) → `atomic_replace_binary` (rename, never write-in-place; failed swap leaves `<binary>.bak`). During agent upgrades, `reconcile_agent_unit_dropin()` heals fleet systemd units to the current template — this is how pre-`AmbientCapabilities` units get fixed without manual fleet surgery ([deployment/01-docker-stack](deployment/01-docker-stack.md)).
|
||||
Agent, helper, and desktop binaries update through the same gate as packages: `stage_and_verify_binary` (hash check before anything moves) → `atomic_replace_binary` (rename, never write-in-place; failed swap leaves `<binary>.bak`). During agent upgrades, `reconcile_agent_unit_dropin()` heals fleet systemd units to the current template — this is how pre-`AmbientCapabilities` units get fixed without manual fleet surgery ([deployment/01-docker-stack](../deployment/01-docker-stack.md)).
|
||||
|
||||
---
|
||||
|
||||
## Standalone Mint Mode
|
||||
|
||||
The helper also carries a local-authority minting path (`MintRequest` / `MintedToken` / `load_mint_key`) for deployments where the signing authority runs beside the host rather than on a central server. Design of record: [security/06-standalone-authority](security/06-standalone-authority.md).
|
||||
The helper also carries a local-authority minting path (`MintRequest` / `MintedToken` / `load_mint_key`) for deployments where the signing authority runs beside the host rather than on a central server. Design of record: [security/06-standalone-authority](../security/06-standalone-authority.md).
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -53,4 +53,4 @@ The helper also carries a local-authority minting path (`MintRequest` / `MintedT
|
|||
|
||||
---
|
||||
|
||||
*Last reviewed: 2026-06-11*
|
||||
*Last reviewed: 2026-06-14*
|
||||
|
|
|
|||
|
|
@ -10,11 +10,11 @@ The desktop app (`desktop/`, Tauri 2) holds no tokens, reads no protected config
|
|||
and never talks to the fleet server. Its entire world is the agent's local API
|
||||
socket, and its authorization is its operating-system identity: the user running it
|
||||
is either in the `redflag-local` group or the kernel refuses the connection. See
|
||||
[security/01-trust-boundaries](security/01-trust-boundaries.md), Local Trust
|
||||
[security/01-trust-boundaries](../security/01-trust-boundaries.md), Local Trust
|
||||
Boundary.
|
||||
|
||||
The credential-less surface is a design decision, not an inevitability — the
|
||||
reasoning lives in [security/06-standalone-authority](security/06-standalone-authority.md):
|
||||
reasoning lives in [security/06-standalone-authority](../security/06-standalone-authority.md):
|
||||
group membership only gates the *request*; the judgment stays in the gates and the
|
||||
root-owned mint key, and same-host token schemes don't reproduce an off-host
|
||||
boundary. The decision is held open in one respect: the socket does not capture
|
||||
|
|
@ -86,6 +86,10 @@ is not yet in release version lockstep; closing that is `docs/tasks/INSTALL-001`
|
|||
|
||||
## Cross-References
|
||||
|
||||
- [security/01-trust-boundaries](security/01-trust-boundaries.md) — the Local trust boundary this client lives behind
|
||||
- [security/01-trust-boundaries](../security/01-trust-boundaries.md) — the Local trust boundary this client lives behind
|
||||
- [components/02-agent](02-agent.md) — the localapi server side
|
||||
- [components/04-helper](04-helper.md) — how the desktop binary itself updates
|
||||
|
||||
---
|
||||
|
||||
*Last reviewed: 2026-06-14*
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ if err != nil {
|
|||
| Pattern | Implementation | When to Use |
|
||||
|---------|----------------|-------------|
|
||||
| Circuit Breaker | `agent/internal/circuitbreaker/circuitbreaker.go` | External APIs, scanners |
|
||||
| Retry with Backoff | `agent/internal/retry/retry.go:calculateBackoff()` | Server unavailable |
|
||||
| Retry with Backoff | `agent/internal/retry/retry.go:calculateDelay()` | Server unavailable |
|
||||
| At-Least-Once Delivery | `pending_acks.json` + retry | Command dispatch |
|
||||
| Buffering | `events_buffer.json` | Network partition |
|
||||
| Atomic Operations | Database transactions | State changes |
|
||||
|
|
@ -125,4 +125,4 @@ DELETE FROM agents WHERE id = ? AND deleted_at IS NULL // Safe to repeat
|
|||
|
||||
---
|
||||
|
||||
*Last reviewed: 2026-05-26*
|
||||
*Last reviewed: 2026-06-14*
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
**Implementation:**
|
||||
- Polling loop: `agent/internal/agent/loop.go`
|
||||
- Backoff logic: `agent/internal/retry/retry.go:calculateBackoff()`
|
||||
- Backoff logic: `agent/internal/retry/retry.go:calculateDelay()`
|
||||
- Heartbeat: `agent/internal/orchestrator/system_scanner.go`
|
||||
|
||||
**Cross-references:**
|
||||
|
|
@ -32,7 +32,7 @@
|
|||
|
||||
## Decision 2: Hardware-Bound Machine IDs
|
||||
|
||||
**Choice:** SHA-256 hash of machineid library output + hostname (Linux-only; Windows/macOS use OS-provided identifiers).
|
||||
**Choice:** SHA-256 hash of the `machineid` library identifier (cross-platform), with OS-specific fallbacks — `/etc/machine-id`, dbus machine-id, DMI product UUID, and hostname only as a last resort on generic platforms.
|
||||
|
||||
**Rationale:**
|
||||
- Prevents config file copying between machines — a stolen agent config cannot be used on a different machine.
|
||||
|
|
@ -46,13 +46,14 @@
|
|||
**Implementation:**
|
||||
```go
|
||||
// agent/internal/system/machine_id.go
|
||||
func GenerateMachineID() (string, error) {
|
||||
machineID, _ := machineid.ID() // Uses machineid library with fallbacks
|
||||
hostname, _ := os.Hostname()
|
||||
|
||||
combined := string(machineID) + hostname
|
||||
hash := sha256.Sum256([]byte(combined))
|
||||
return hex.EncodeToString(hash[:]), nil
|
||||
func GetMachineID() (string, error) {
|
||||
id, err := machineid.ID() // cross-platform; Linux reads /etc/machine-id
|
||||
if err == nil && id != "" {
|
||||
return hashMachineID(id), nil // SHA-256, hex-encoded — no hostname
|
||||
}
|
||||
// OS-specific fallbacks: /etc/machine-id, dbus machine-id, DMI product UUID;
|
||||
// hostname only as a last resort on generic/unknown platforms
|
||||
return osSpecificFallback()
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -133,7 +134,7 @@ type CircuitBreaker struct {
|
|||
|
||||
**Cross-references:**
|
||||
- `flows/02-command-execution.md` (circuit breaker integration in polling loop)
|
||||
- `testing/02-circuit-breaker.md` (circuit breaker tests)
|
||||
- `testing/01-test-pyramid.md` (circuit breaker unit tests)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -257,9 +258,6 @@ func (c *CommandHandler) handleCommand(cmd *Command) {
|
|||
- No multi-tenant support (single-tenant by design)
|
||||
- Requires operational overhead (updates, backups, maintenance)
|
||||
|
||||
**Cross-references:**
|
||||
- `reference/01-connectwise-comparison.md` (competitive positioning)
|
||||
|
||||
---
|
||||
|
||||
## Decision 10: Update Nonce for Replay Protection
|
||||
|
|
@ -369,7 +367,7 @@ func (h *SubsystemHandler) DisableSubsystem(...) {
|
|||
|
||||
---
|
||||
|
||||
*Last reviewed: 2026-05-26*
|
||||
*Last reviewed: 2026-06-14*
|
||||
|
||||
**Footer: Assumptions & Connections**
|
||||
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@
|
|||
Every HTTP endpoint must be classified by who is allowed to call it and which middleware enforces that classification.
|
||||
|
||||
**Cross-references:**
|
||||
- [security/02-authentication-stack](security/02-authentication-stack.md) (auth layers)
|
||||
- [security/03-refresh-tokens](security/03-refresh-tokens.md) (token lifecycle)
|
||||
- [security/04-machine-binding](security/04-machine-binding.md) (machine ID binding)
|
||||
- [security/02-authentication-stack](02-authentication-stack.md) (auth layers)
|
||||
- [security/03-refresh-tokens](03-refresh-tokens.md) (token lifecycle)
|
||||
- [security/04-machine-binding](04-machine-binding.md) (machine ID binding)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -75,8 +75,8 @@ Grace is *structural*, not timed: "successor still unconsumed" is the discrimina
|
|||
Ties to SEC-012 (renewal atomicity): the server side is now fully transactional; the agent↔server two-phase (server commits rotation / agent persists token) is reconciled by the grace path rather than true cross-network atomicity.
|
||||
|
||||
**Cross-references:**
|
||||
- [flows/01-registration](flows/01-registration.md) (registration flow)
|
||||
- [flows/03-agent-upgrade](flows/03-agent-upgrade.md) (download endpoint)
|
||||
- [flows/01-registration](../flows/01-registration.md) (registration flow)
|
||||
- [flows/03-agent-upgrade](../flows/03-agent-upgrade.md) (download endpoint)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -94,8 +94,8 @@ Ties to SEC-012 (renewal atomicity): the server side is now fully transactional;
|
|||
- **Download endpoint** (`GET /api/v1/downloads/updates/:package_id`) requires machine binding to prevent any authenticated agent from downloading any package
|
||||
|
||||
**Cross-references:**
|
||||
- [security/02-authentication-stack](security/02-authentication-stack.md) (JWT validation)
|
||||
- [security/04-machine-binding](security/04-machine-binding.md) (machine ID validation)
|
||||
- [security/02-authentication-stack](02-authentication-stack.md) (JWT validation)
|
||||
- [security/04-machine-binding](04-machine-binding.md) (machine ID validation)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -109,7 +109,7 @@ Ties to SEC-012 (renewal atomicity): the server side is now fully transactional;
|
|||
- Admin-only routes use `AdminRoleMiddleware`
|
||||
|
||||
**Cross-references:**
|
||||
- [security/02-authentication-stack](security/02-authentication-stack.md) (web JWT)
|
||||
- [security/02-authentication-stack](02-authentication-stack.md) (web JWT)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -125,7 +125,7 @@ Ties to SEC-012 (renewal atomicity): the server side is now fully transactional;
|
|||
- Can trigger machine rebind
|
||||
|
||||
**Cross-references:**
|
||||
- [security/03-refresh-tokens](security/03-refresh-tokens.md) (token revocation)
|
||||
- [security/03-refresh-tokens](03-refresh-tokens.md) (token revocation)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -159,7 +159,7 @@ upgrade path re-asserts socket-chain ownership but does not re-check membership
|
|||
(tracked: INSTALL-001 post-install healthcheck).
|
||||
|
||||
**Cross-references:**
|
||||
- [components/05-desktop](components/05-desktop.md) (the only intended client)
|
||||
- [components/05-desktop](../components/05-desktop.md) (the only intended client)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -172,8 +172,8 @@ upgrade path re-asserts socket-chain ownership but does not re-check membership
|
|||
**Fix:** Moved endpoint to `admin-only` group with `WebAuthMiddleware + AdminRoleMiddleware`.
|
||||
|
||||
**Cross-references:**
|
||||
- [core/01-ethos](core/01-ethos.md) (principle #2: Security is Non-Negotiable)
|
||||
- [flows/02-command-execution](flows/02-command-execution.md) (polling loop)
|
||||
- [core/01-ethos](../core/01-ethos.md) (principle #2: Security is Non-Negotiable)
|
||||
- [flows/02-command-execution](../flows/02-command-execution.md) (polling loop)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -197,7 +197,7 @@ constructor call or unregistered wrapper flags its routes at boot instead of pas
|
|||
them silently.
|
||||
|
||||
**Cross-references:**
|
||||
- [core/01-ethos](core/01-ethos.md) (principle #2: no unauthenticated endpoints)
|
||||
- [core/01-ethos](../core/01-ethos.md) (principle #2: no unauthenticated endpoints)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -225,12 +225,12 @@ bounded by what agents choose to fetch and verify.
|
|||
|
||||
## Cross-References
|
||||
|
||||
- **Auth layers** → [security/02-authentication-stack](security/02-authentication-stack.md)
|
||||
- **Refresh tokens** → [security/03-refresh-tokens](security/03-refresh-tokens.md)
|
||||
- **Machine binding** → [security/04-machine-binding](security/04-machine-binding.md)
|
||||
- **Registration** → [flows/01-registration](flows/01-registration.md)
|
||||
- **Command execution** → [flows/02-command-execution](flows/02-command-execution.md)
|
||||
- **Auth layers** → [security/02-authentication-stack](02-authentication-stack.md)
|
||||
- **Refresh tokens** → [security/03-refresh-tokens](03-refresh-tokens.md)
|
||||
- **Machine binding** → [security/04-machine-binding](04-machine-binding.md)
|
||||
- **Registration** → [flows/01-registration](../flows/01-registration.md)
|
||||
- **Command execution** → [flows/02-command-execution](../flows/02-command-execution.md)
|
||||
|
||||
---
|
||||
|
||||
*Last reviewed: 2026-06-11*
|
||||
*Last reviewed: 2026-06-14*
|
||||
|
|
@ -42,8 +42,8 @@
|
|||
```
|
||||
|
||||
**Cross-references:**
|
||||
- [flows/01-registration](flows/01-registration.md) (registration flow)
|
||||
- [security/03-refresh-tokens](security/03-refresh-tokens.md) (refresh tokens)
|
||||
- [flows/01-registration](../flows/01-registration.md) (registration flow)
|
||||
- [security/03-refresh-tokens](03-refresh-tokens.md) (refresh tokens)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -113,8 +113,8 @@ func AuthMiddleware() gin.HandlerFunc {
|
|||
```
|
||||
|
||||
**Cross-references:**
|
||||
- [security/01-trust-boundaries](security/01-trust-boundaries.md) (trust boundary matrix)
|
||||
- [flows/02-command-execution](flows/02-command-execution.md) (polling loop)
|
||||
- [security/01-trust-boundaries](01-trust-boundaries.md) (trust boundary matrix)
|
||||
- [flows/02-command-execution](../flows/02-command-execution.md) (polling loop)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -162,8 +162,8 @@ Headers: `X-Machine-ID` (required), `Content-Type: application/json`
|
|||
The agent must persist `refresh_token` to disk; if it crashes before doing so, accept-previous-once grace recovers on the next attempt.
|
||||
|
||||
**Cross-references:**
|
||||
- [flows/01-registration](flows/01-registration.md) (registration flow)
|
||||
- [flows/02-command-execution](flows/02-command-execution.md) (renewal in polling loop)
|
||||
- [flows/01-registration](../flows/01-registration.md) (registration flow)
|
||||
- [flows/02-command-execution](../flows/02-command-execution.md) (renewal in polling loop)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -183,8 +183,8 @@ The agent must persist `refresh_token` to disk; if it crashes before doing so, a
|
|||
**Middleware:** `server/internal/api/middleware/machine_binding.go`
|
||||
|
||||
**Cross-references:**
|
||||
- [security/01-trust-boundaries](security/01-trust-boundaries.md) (trust boundary matrix)
|
||||
- [flows/01-registration](flows/01-registration.md) (machine ID generation)
|
||||
- [security/01-trust-boundaries](01-trust-boundaries.md) (trust boundary matrix)
|
||||
- [flows/01-registration](../flows/01-registration.md) (machine ID generation)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -192,17 +192,14 @@ The agent must persist `refresh_token` to disk; if it crashes before doing so, a
|
|||
|
||||
**Assumption:** Trust On First Use (TOFU) model — agent caches server public key at registration and uses it for all future verification.
|
||||
|
||||
**Connection:** [security/01-trust-boundaries](security/01-trust-boundaries.md) (trust boundary matrix)
|
||||
**Connection:** [security/01-trust-boundaries](01-trust-boundaries.md) (trust boundary matrix)
|
||||
|
||||
**Connection:** [security/04-machine-binding](security/04-machine-binding.md) (hardware-bound auth)
|
||||
**Connection:** [security/04-machine-binding](04-machine-binding.md) (hardware-bound auth)
|
||||
|
||||
**Connection:** [verification/01-signing-pipeline](verification/01-signing-pipeline.md) (Ed25519 signing)
|
||||
**Connection:** [verification/01-signing-pipeline](../verification/01-signing-pipeline.md) (Ed25519 signing)
|
||||
|
||||
**Connection:** [verification/02-agent-verification](verification/02-agent-verification.md) (command verification)
|
||||
**Connection:** [verification/02-agent-verification](../verification/02-agent-verification.md) (command verification)
|
||||
|
||||
---
|
||||
|
||||
*Last reviewed: 2026-05-26*
|
||||
---
|
||||
|
||||
*Last reviewed: 2026-05-26*
|
||||
*Last reviewed: 2026-06-14*
|
||||
|
|
@ -9,8 +9,8 @@
|
|||
Agents authenticate with short-lived JWTs minted against a long-lived refresh token (90-day TTL). Every renewal *rotates* the refresh token: a successor is minted, the old token is marked consumed. The rotation lineage is the security mechanism — replaying a consumed token is how theft announces itself.
|
||||
|
||||
**Cross-references:**
|
||||
- [security/02-authentication-stack](security/02-authentication-stack.md) (where this sits in the four-layer stack)
|
||||
- [security/04-machine-binding](security/04-machine-binding.md) (the renewal endpoint is machine-bound)
|
||||
- [security/02-authentication-stack](02-authentication-stack.md) (where this sits in the four-layer stack)
|
||||
- [security/04-machine-binding](04-machine-binding.md) (the renewal endpoint is machine-bound)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -42,13 +42,13 @@ Tokens are stored hashed (`HashRefreshToken`), never plaintext. Queries live in
|
|||
| Consumed, successor **unconsumed** | Crash-recovery grace | Agent saved the old token but died before persisting the new one. Accept once; issue a fresh successor |
|
||||
| Consumed, successor **also consumed** | Reuse = theft | Revoke the entire `family_id`, log security event, return terminal error |
|
||||
|
||||
The grace window is **accept-previous-once** — exactly one step back in the lineage, exactly once. Forward-only is doctrine ([core/01-ethos](core/01-ethos.md)); there is no knob to widen it.
|
||||
The grace window is **accept-previous-once** — exactly one step back in the lineage, exactly once. Forward-only is doctrine ([core/01-ethos](../core/01-ethos.md)); there is no knob to widen it.
|
||||
|
||||
---
|
||||
|
||||
## Agent Side
|
||||
|
||||
- Terminal sentinel errors (`ErrRefreshTokenInvalid`, `ErrMachineMismatch`) stop the polling loop's retry machinery — these are not transient network failures and are logged `[CRITICAL]`. See [components/02-agent](components/02-agent.md).
|
||||
- Terminal sentinel errors (`ErrRefreshTokenInvalid`, `ErrMachineMismatch`) stop the polling loop's retry machinery — these are not transient network failures and are logged `[CRITICAL]`. See [components/02-agent](../components/02-agent.md).
|
||||
- The **instance lock** exists largely for this mechanism: two agent processes sharing one `config.json` would race rotations and trip family revocation on themselves. One config, one process, enforced by flock/mutex.
|
||||
|
||||
---
|
||||
|
|
@ -67,4 +67,4 @@ A refresh token in a file on a fleet machine *will* eventually leak — backup s
|
|||
|
||||
---
|
||||
|
||||
*Last reviewed: 2026-06-11*
|
||||
*Last reviewed: 2026-06-14*
|
||||
|
|
|
|||
|
|
@ -367,10 +367,10 @@ are defense-in-depth on top, not a prerequisite. Userspace wrappers alone are by
|
|||
shell, and a stripped environment; the agent that hands it tokens is unprivileged and holds no
|
||||
signing key.
|
||||
|
||||
**Connection:** [security/01-trust-boundaries](security/01-trust-boundaries.md) (kernel enforcement as trust boundary)
|
||||
**Connection:** [security/01-trust-boundaries](01-trust-boundaries.md) (kernel enforcement as trust boundary)
|
||||
|
||||
**Connection:** [security/04-machine-binding](security/04-machine-binding.md) (agent identity verification)
|
||||
**Connection:** [security/04-machine-binding](04-machine-binding.md) (agent identity verification)
|
||||
|
||||
---
|
||||
|
||||
*Last reviewed: 2026-06-11*
|
||||
*Last reviewed: 2026-06-14*
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@
|
|||
All commands and binaries are signed with Ed25519 private key on the server. Agents verify signatures before execution.
|
||||
|
||||
**Cross-references:**
|
||||
- [verification/02-agent-verification](verification/02-agent-verification.md) (agent-side verification)
|
||||
- [verification/03-key-rotation](verification/03-key-rotation.md) (key rotation support)
|
||||
- [security/01-trust-boundaries](security/01-trust-boundaries.md) (signature as trust boundary)
|
||||
- [verification/02-agent-verification](02-agent-verification.md) (agent-side verification)
|
||||
- [verification/03-key-rotation](03-key-rotation.md) (key rotation support)
|
||||
- [security/01-trust-boundaries](../security/01-trust-boundaries.md) (signature as trust boundary)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -166,12 +166,12 @@ func (s *BuildOrchestratorService) BuildAndSignAgent(version, platform, architec
|
|||
|
||||
**Assumption:** Ed25519 signing is enabled when `REDFLAG_SIGNING_PRIVATE_KEY` environment variable is set.
|
||||
|
||||
**Connection:** [verification/02-agent-verification](verification/02-agent-verification.md) (agent-side signature verification)
|
||||
**Connection:** [verification/02-agent-verification](02-agent-verification.md) (agent-side signature verification)
|
||||
|
||||
**Connection:** [verification/03-key-rotation](verification/03-key-rotation.md) (multi-key rotation support)
|
||||
**Connection:** [verification/03-key-rotation](03-key-rotation.md) (multi-key rotation support)
|
||||
|
||||
**Connection:** [security/01-trust-boundaries](security/01-trust-boundaries.md) (cryptographic trust boundary)
|
||||
**Connection:** [security/01-trust-boundaries](../security/01-trust-boundaries.md) (cryptographic trust boundary)
|
||||
|
||||
---
|
||||
|
||||
*Last reviewed: 2026-05-26*
|
||||
*Last reviewed: 2026-06-14*
|
||||
|
|
@ -9,9 +9,9 @@
|
|||
Agent verifies Ed25519 signatures, timestamps, and nonces before executing commands.
|
||||
|
||||
**Cross-references:**
|
||||
- [verification/01-signing-pipeline](verification/01-signing-pipeline.md) (server-side signing)
|
||||
- [verification/03-key-rotation](verification/03-key-rotation.md) (key rotation)
|
||||
- [verification/04-replay-protection](verification/04-replay-protection.md) (replay protection)
|
||||
- [verification/01-signing-pipeline](01-signing-pipeline.md) (server-side signing)
|
||||
- [verification/03-key-rotation](03-key-rotation.md) (key rotation)
|
||||
- [verification/04-replay-protection](04-replay-protection.md) (replay protection)
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -152,11 +152,11 @@ func (h *UpdateHandler) verifyDownload(binaryData []byte, checksum string) error
|
|||
|
||||
## Cross-References
|
||||
|
||||
- **Server signing** → [verification/01-signing-pipeline](verification/01-signing-pipeline.md)
|
||||
- **Key rotation** → [verification/03-key-rotation](verification/03-key-rotation.md)
|
||||
- **Replay protection** → [verification/04-replay-protection](verification/04-replay-protection.md)
|
||||
- **Install script** → [flows/01-registration](flows/01-registration.md) (TOFU key caching)
|
||||
- **Server signing** → [verification/01-signing-pipeline](01-signing-pipeline.md)
|
||||
- **Key rotation** → [verification/03-key-rotation](03-key-rotation.md)
|
||||
- **Replay protection** → [verification/04-replay-protection](04-replay-protection.md)
|
||||
- **Install script** → [flows/01-registration](../flows/01-registration.md) (TOFU key caching)
|
||||
|
||||
---
|
||||
|
||||
*Last reviewed: 2026-05-26*
|
||||
*Last reviewed: 2026-06-14*
|
||||
Loading…
Reference in a new issue