-
GitHub-native control plane
+
GitHub-native command and control
GitHub is the transport.Trust is the protocol.
- OctoC2 connects a TypeScript beacon, durable controller, local dashboard,
- and operator CLI through encrypted, authenticated transport paths.
+ OctoC2 is built for authorized security research, connecting a TypeScript
+ beacon, durable controller, local dashboard, and operator CLI through
+ encrypted multi-channel transport with resilient failover.
-
Transport selection never changes task ownership or signature requirements. Review exact permissions and prerequisites in the quickstart before enabling a channel.
+
Transport selection never changes task ownership or signature requirements. Review exact permissions, credentials, and environment prerequisites in the channel guide before enabling a path.
@@ -366,8 +416,8 @@ function App() {
{guides.map((guide) => (
@@ -385,12 +435,14 @@ function App() {
-
OctoC2 GitHub-native systems research
+
OctoC2 Encrypted multi-channel operations
Use only on systems and repositories you are explicitly authorized to test.
diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md
new file mode 100644
index 0000000..6d7aaeb
--- /dev/null
+++ b/docs/ARCHITECTURE.md
@@ -0,0 +1,137 @@
+# Architecture
+
+OctoC2 is a multi-transport control plane built around one signed task
+protocol. The transport may change during failover, but task identity,
+encryption, ownership, replay protection, and result acceptance do not.
+
+> [!IMPORTANT]
+> **Authorized use only.** Apply these components and trust boundaries only to
+> systems and repositories you own or have explicit permission to test.
+
+## Component model
+
+```text
+Dashboard / octoctl
+ │ authenticated operator API or scoped GitHub API
+ ▼
+Controller ─────────────── durable SQLite state
+ │ identities · tasks · delivery leases
+ │ signed and results · replay records · cursors
+ │ encrypted envelopes
+ ▼
+GitHub APIs · HTTPS/WSS · gRPC/mTLS · signed relays
+ │
+ ▼
+Pre-enrolled beacon ────── persistent identity and task ledger
+```
+
+| Component | Responsibility |
+|---|---|
+| Beacon (`implant/`) | Select transports, check in, decrypt and validate tasks, execute catalogued handlers, sign results, and recover configuration |
+| Controller (`server/`) | Register identities, queue tasks, grant delivery leases, poll channels, verify results, persist state, and expose operator/direct APIs |
+| Dashboard (`dashboard/`) | Present beacon health, task state, results, activity, and transport views to an authenticated operator |
+| CLI (`octoctl/`) | Generate keys, provision environments, enroll/build beacons, queue tasks, inspect results, manage recovery, and configure proxies |
+| Shared contracts (`shared/`) | Define canonical channel/task catalogs, signed envelopes, key IDs, validation rules, and result receipts |
+
+## Identity and cryptography
+
+OctoC2 deliberately separates cryptographic roles:
+
+| Identity | Algorithm | Trust boundary |
+|---|---|---|
+| Operator encryption | X25519 / libsodium `crypto_box` | Tasks are sealed to a beacon; results are sealed to the operator |
+| Beacon signing | Ed25519 | Check-ins and results are bound to the enrolled beacon identity |
+| Recovery signing | Ed25519 | Dead-drop records and key transitions are authenticated independently of GitHub |
+| TLS server identity | X.509 | HTTPS/gRPC endpoint name and CA trust |
+| gRPC client identity | X.509 + SHA-256 fingerprint binding | Exact certificate-to-beacon binding |
+| GitHub identity | App installation token, PAT, or OIDC claims | Authorization to the selected GitHub surface |
+| Direct API identity | Operator or per-beacon bearer token | Controller route authorization |
+
+Encryption does not replace authentication. A result is accepted only after
+the signed envelope, enrolled beacon key, beacon/task ownership, task state,
+replay record, and result digest all agree.
+
+## Task lifecycle
+
+1. An authenticated operator submits a catalogued task for a known beacon.
+2. The controller validates arguments, seals the task to that beacon, signs the
+ envelope, and persists it as `pending`.
+3. One eligible channel receives an exclusive delivery lease and publishes or
+ returns the task.
+4. The beacon verifies and decrypts the envelope, records the task in its
+ durable ledger, and dispatches the matching handler once.
+5. The beacon signs and encrypts the result, then returns it through the active
+ channel.
+6. The controller verifies identity and ownership, persists the result, marks
+ the task complete or failed, and emits a result-acceptance receipt where the
+ channel supports acknowledgements.
+
+Delivery attempts may repeat; execution may not. The durable controller state,
+exclusive leases, replay store, and beacon ledger make retries safe across
+process restarts.
+
+## Transport abstraction and failover
+
+`ConnectionFactory` builds the configured tentacles, records health, applies
+the priority list, and moves to an eligible fallback after a classified
+failure. A transport is eligible only when its runtime configuration and the
+canonical channel prerequisites are satisfied.
+
+GitHub transports exchange the same encrypted payload through different
+artifacts. Direct transports use HTTPS or gRPC but preserve the same envelope
+and ownership checks. Proxy and relay paths add signed routing; they do not
+become a new source of task authority.
+
+See [Channels](CHANNELS.md) for prerequisites and status, and [Operations and
+assurance](PRODUCTION.md) for failover, listener, and acceptance policy.
+
+## Durable state
+
+The controller stores beacon identities and lifecycle status, queued tasks,
+delivery leases, results, processed-envelope replay records, GitHub cursors,
+and channel-specific state in its configured data directory. The beacon stores
+its signing identity and task ledger locally.
+
+Back up and restore these as security state, not disposable cache. Restoring
+only part of the state can invalidate identity or replay assumptions. Never
+copy one beacon's identity directory to another host.
+
+## Recovery
+
+When ordinary channels cannot authenticate, `DeadDropResolver` searches a
+dedicated public recovery repository for a deterministic record. Each record
+is signed by the recovery identity, sealed to one beacon, generation-numbered,
+time-bounded, and constrained by the controller's complete replacement policy.
+
+A valid record can replace transport configuration and provide a narrowed,
+short-lived GitHub App installation-token lease. It cannot grant authority
+outside the server's exact per-beacon policy. See [Recovery](RECOVERY.md).
+
+## Feature boundaries
+
+- Thirteen channel kinds are selectable; the reserved PR+SSH catalog entry is
+ intentionally unavailable.
+- Six task kinds are accepted: `shell`, `exec`, `ping`, `sleep`, `kill`, and
+ `evasion`. Argument schemas and risk levels are centralized in `shared/`.
+- Remote module loading is rejected across public surfaces.
+- HTTP and gRPC listeners are opt-in. HTTP requires TLS; gRPC requires mTLS and
+ a per-beacon bearer credential.
+- App private keys and recovery signing secrets are controller-only.
+- Channel availability is an environment claim, not a catalog claim. Local
+ tests, integration tests, and live task/result qualification are reported as
+ separate evidence levels.
+
+## Trust boundaries
+
+| Boundary | Required control |
+|---|---|
+| Operator → controller | Operator-only API token, trusted TLS when non-loopback |
+| Beacon → controller | Per-beacon bearer, trusted TLS, and mTLS/fingerprint binding for gRPC |
+| Component → GitHub | Least-privilege role credential scoped to exact repositories |
+| Controller → beacon | Signed task, X25519 sealing, enrolled recipient identity |
+| Beacon → controller | Signed result, ownership check, replay rejection, acceptance digest |
+| Recovery repo → beacon | Recovery signature, exact beacon recipient, generation and expiry checks |
+| Proxy/relay → endpoints | Signed route/configuration and exact repository or certificate bindings |
+
+For a deployment checklist and failure policy, continue with [Operations and
+assurance](PRODUCTION.md).
diff --git a/docs/CHANNELS.md b/docs/CHANNELS.md
new file mode 100644
index 0000000..1a475c0
--- /dev/null
+++ b/docs/CHANNELS.md
@@ -0,0 +1,203 @@
+# Channel Guide
+
+OctoC2 calls each communication implementation a **tentacle**. All selectable
+tentacles carry the canonical signed and encrypted task protocol; they differ
+in artifact type, authentication, latency, and operational prerequisites.
+
+> [!IMPORTANT]
+> **Authorized use only.** Enable a channel only across repositories, accounts,
+> networks, and endpoints included in the approved test boundary.
+
+## Status vocabulary
+
+| Status | Meaning |
+|---|---|
+| Implemented | Complete implant and controller counterparts are present and covered by repository tests |
+| Selectable | Runtime code exists, but successful use depends on explicit environment, identity, network, or external-service qualification |
+| Unavailable | Reserved identifier; runtime selection is rejected |
+
+Selectable does not mean live-qualified in every environment. Use the
+[traceability record](REMEDIATION_TRACEABILITY.md) to distinguish local tests,
+integration tests, and live task/result evidence for the exact revision.
+
+## Catalog
+
+| ID | Kind | Path | Status | Principal prerequisites |
+|---:|---|---|---|---|
+| 1 | `issues` | GitHub issue comments | Implemented | Metadata read, Issues read/write, operator public key |
+| 2 | `branch` | Per-beacon branch/files | Implemented | Metadata read, Contents read/write, default branch, operator public key |
+| 3 | `actions` | Dispatch + Actions variables | Implemented | Metadata read, Actions and Variables read/write, operator public key |
+| 4 | `codespaces` | gRPC over Codespaces SSH | Selectable | gRPC/mTLS, beacon bearer, SSH, user Codespaces token |
+| 5 | `pages` | Deployments/statuses | Selectable | Metadata read, Deployments read/write, default branch |
+| 6 | `gist` | Secret Gists | Implemented | Two distinct `gist` PATs for one dedicated account |
+| 7 | `oidc` | HTTPS with Actions OIDC | Selectable | Actions `id-token: write`, HTTPS/TLS, exact claims binding |
+| 7b | `secrets` | Actions variables | Selectable | Metadata read, Variables read/write, operator public key |
+| 8 | `pull_request` | Reserved PR+SSH path | Unavailable | Not selectable |
+| 9 | `stego` | LSB payload in PNG files | Selectable | Metadata read, Contents read/write, default branch |
+| 10 | `proxy` | Signed control/decoy relay | Selectable | Distinct private decoy repo, Issues transport, signed route |
+| 11 | `notes` | Git Notes refs | Implemented | Metadata read, Contents read/write, operator public key |
+| 12 | `relay` | Authenticated gRPC/SSH consortium | Selectable | Signed relay config, gRPC/mTLS, bearer, optional SSH discovery |
+| 13 | `http` | Direct HTTPS/WebSocket | Selectable | Opt-in HTTPS listener, trusted server certificate, beacon bearer |
+
+## Repository-backed channels
+
+### Issues
+
+Issues publishes encrypted tasks and results as comments. Install the App or
+scope fine-grained tokens to the control repository with Metadata read and
+Issues read/write. Set the `MONITORING_PUBKEY` Actions variable. A `404` often
+means the token cannot see the private repo or the referenced issue was removed;
+a `403` usually indicates permission or policy denial.
+
+### Branch
+
+Branch stores per-beacon transport files on a dedicated branch. It requires
+Contents read/write and a resolvable default branch. The implementation can
+bootstrap a missing transport branch; protection rules must still permit the
+configured identity to write the transport paths.
+
+### Actions
+
+Actions combines repository dispatch with Actions variables. Grant both
+Actions read/write and Variables read/write. Repository dispatch is accepted
+only when sent to the exact configured repository; workflow permissions and
+organization policy can still block it.
+
+### Pages
+
+Pages uses GitHub deployments and deployment statuses, not a public Pages site.
+Grant Deployments read/write and ensure the repository has a default branch.
+No public website needs to be enabled.
+
+### Secrets (`7b`)
+
+The historical channel name is `secrets`, but its transport uses Actions
+**variables**, not repository secret values. Grant Variables read/write. Do not
+put transport ciphertext in Actions secrets under the assumption that this
+channel reads them.
+
+### Steganography
+
+Stego embeds encrypted payload bytes in PNG files and commits them through the
+Contents API. It requires the same repository and key prerequisites as Branch.
+Verify that security tooling, image optimization, or mirroring does not rewrite
+the PNG, because byte transformation destroys the embedded payload.
+
+### Git Notes
+
+Notes writes encrypted records under Git Notes refs using Contents access. Repo
+mirrors and cleanup jobs do not always preserve notes refs; include those refs
+in backup and cleanup procedures.
+
+## User-level GitHub channels
+
+### Gists
+
+Gist uses secret Gists belonging to one dedicated GitHub account. Configure two
+different classic PATs with `gist`: one on the controller and one on the
+beacon. The repository App installation lease is not used. Remove test Gists
+after a live qualification, but retain or revoke the PATs only according to the
+operator's credential cleanup decision.
+
+### Codespaces
+
+Codespaces discovers an approved Codespace through the GitHub API and opens an
+SSH tunnel to the controller's gRPC listener. It requires:
+
+- a classic PAT with `codespace` for the account that can access the named
+ Codespace;
+- GitHub CLI authentication and SSH connectivity;
+- gRPC server CA/certificate/key;
+- a unique client certificate and exact fingerprint binding for the beacon;
+- a per-beacon bearer credential.
+
+Direct gRPC can be used without Codespaces discovery by setting
+`SVC_GRPC_DIRECT`. Codespaces never falls back to a repository App lease for its
+user-level API.
+
+## Direct channels
+
+### HTTP
+
+HTTP uses the controller's opt-in HTTPS/WebSocket listener. Configure a server
+certificate whose SAN covers the hostname in `SVC_HTTP_URL`, install the CA in
+the beacon trust path, and give the beacon its exact bearer token. Credentials
+are accepted in headers, not URLs. There is no plaintext or certificate-bypass
+mode.
+
+### OIDC
+
+OIDC is intended for GitHub Actions jobs. The workflow requests
+`id-token: write`, obtains an identity token for the configured audience, and
+connects to the HTTPS controller. The controller binding must match repository,
+beacon ID, subject, and workflow ref exactly; empty lists and wildcards are
+rejected. OIDC proves the workflow identity and does not broaden task or result
+ownership.
+
+### Relay
+
+Relay uses an explicitly provisioned consortium route over authenticated gRPC
+and optional SSH discovery. Each hop must satisfy the same mTLS, bearer, and
+identity requirements as direct gRPC. Relay configuration is signed; discovery
+data is not itself task authority.
+
+## Proxy channel
+
+Proxy wraps the Issues channel in a signed two-repository route. The beacon
+interacts with a private decoy repository; a workflow relays envelopes to the
+private control repository and returns results. Provision both sides with
+`octoctl proxy create` or `proxy provision`, then follow the [proxy workflow
+contract](../templates/proxy/README.md).
+
+The control and decoy repositories must be different. Use exact repository
+bindings, distinct dispatch credentials, signed envelopes, and route-level
+deduplication. A proxy test is complete only after the live result is accepted
+and relay artifacts are cleaned from both repositories.
+
+## Selection and failover
+
+Set a comma-separated priority list:
+
+```text
+SVC_TENTACLE_PRIORITY=issues,branch,actions,http
+```
+
+Unknown and unavailable channel names are logged and ignored; if none remain,
+the beacon falls back to Issues. Configure every listed channel completely and
+review startup logs for ignored entries. `ConnectionFactory` selects healthy
+channels in order, classifies failures, and retries or fails over according to
+channel policy.
+
+Inspect configuration and health with:
+
+```bash
+octoctl tentacles list
+octoctl tentacles health
+```
+
+Force a specific route for an authorized qualification:
+
+```bash
+octoctl task --kind ping --tentacle notes
+octoctl results --last 1
+```
+
+A successful registration alone is not channel qualification. Record encrypted
+task publication, beacon receipt, signed result publication, controller
+acceptance, and artifact cleanup through the same named channel.
+
+## Choosing a channel
+
+| Need | Prefer |
+|---|---|
+| Simplest private-repo baseline | Issues |
+| Repository path without issue artifacts | Branch or Git Notes |
+| GitHub Actions integration | Actions or OIDC |
+| No control-repo visibility at the endpoint | Proxy with a distinct decoy repo |
+| Low-latency private network path | HTTPS or direct gRPC |
+| Gist-only user surface | Gist with dedicated credentials |
+| Recovery after normal credentials fail | Signed recovery record, then the recovered eligible channel |
+
+Choose the least-complex channel that satisfies the approved test boundary.
+Adding channels increases credentials, permissions, artifacts, and cleanup
+obligations.
diff --git a/docs/CLI.md b/docs/CLI.md
new file mode 100644
index 0000000..467d223
--- /dev/null
+++ b/docs/CLI.md
@@ -0,0 +1,240 @@
+# CLI Reference
+
+`octoctl` is the operator interface for setup, enrollment, builds, tasking,
+results, channel health, proxy routes, and local services.
+
+> [!IMPORTANT]
+> **Authorized use only.** CLI commands can change remote repositories and
+> endpoint state. Verify the target and task risk before submission.
+
+Run it from source:
+
+```bash
+cd octoctl
+bun run src/index.ts --help
+```
+
+Examples below use `octoctl` as an alias for that command. Every command
+supports `--help`.
+
+## Operator environment
+
+| Variable | Used for |
+|---|---|
+| `OCTOC2_SERVER_URL` | HTTPS controller origin for live operator commands |
+| `OCTOC2_HTTP_CA_CERT` | CA file used to verify the controller certificate |
+| `OCTOC2_OPERATOR_API_TOKEN` | Operator authentication to controller REST/SSE routes |
+| `OCTOC2_OPERATOR_GITHUB_TOKEN` | Direct operator access to the configured GitHub repo |
+| `OCTOC2_REPO_OWNER`, `OCTOC2_REPO_NAME` | Control repository coordinates |
+| `OCTOC2_OPERATOR_SECRET` | Base64url X25519 secret for task/result cryptography |
+| `OCTOC2_DATA_DIR` | Offline controller data directory (default `./data`) |
+
+Prefer environment variables or protected files over command-line token
+options, because process arguments may be observable by other local users.
+
+## Setup and keys
+
+### `setup`
+
+Run the guided deployment workflow:
+
+```bash
+octoctl setup
+octoctl setup --phase validate
+```
+
+Available phases are `credentials`, `validate`, `keygen`, `auth`, `tentacles`,
+`env`, `build`, and `verify`. A phase is useful when revisiting one part of an
+existing configuration.
+
+### `keygen`
+
+Generate an operator X25519 keypair:
+
+```bash
+octoctl keygen
+octoctl keygen --set-variable
+```
+
+`--set-variable` writes the public key to the control repository's
+`MONITORING_PUBKEY` Actions variable and requires the direct operator GitHub
+credential. Store the printed secret outside source control.
+
+## Beacon inventory and health
+
+```bash
+octoctl beacons
+octoctl beacons --status active
+octoctl beacons --json --data-dir /secure/octoc2-data
+```
+
+Status filters are `active`, `dormant`, and `lost`.
+
+Inspect one beacon's channel activity from durable local state or the live API:
+
+```bash
+octoctl tentacles list --beacon
+octoctl tentacles health --beacon --server-url https://controller.example:8080
+octoctl tentacles list --beacon --verbose --json
+```
+
+`health` is an alias for `list`. Observed activity is not proof that every
+channel prerequisite is currently satisfied.
+
+## Queue tasks
+
+```bash
+octoctl task --kind ping
+octoctl task --kind shell --cmd "whoami"
+octoctl task --kind exec --args-json '{"cmd":"id","args":["-u"]}'
+octoctl task --kind sleep --seconds 300
+octoctl task --kind ping --tentacle notes
+```
+
+Use `--args-json` for advanced schemas. `--tentacle` pins the delivery attempt
+to one selectable channel and is useful for qualification; omit it for normal
+priority/failover behavior.
+
+### Task catalog
+
+| Kind | Risk | Required arguments | Purpose |
+|---|---|---|---|
+| `ping` | Routine | `{}` | Connectivity and process metadata probe |
+| `sleep` | Routine | `seconds`; optional `jitter` | Change check-in timing |
+| `shell` | Elevated | `cmd`; optional `cwd`, `timeout` | Run through the platform shell |
+| `exec` | Elevated | `cmd`; optional `args`, `cwd`, `timeout` | Execute a program directly |
+| `kill` | Destructive | `{}` | Terminate the beacon process |
+| `evasion` | Destructive | Explicit action-specific schema | Invoke an implemented lifecycle/evasion action |
+
+The shared validator rejects unknown fields, invalid types, non-finite values,
+commands longer than 32 KiB, paths longer than 4 KiB, timeouts beyond five
+minutes, and sleep values outside the accepted bounds. `load-module` is not an
+accepted task kind.
+
+Treat `shell`, `exec`, `kill`, and `evasion` as change-controlled operations.
+Use `ping` for initial transport verification.
+
+## Fetch results
+
+```bash
+octoctl results
+octoctl results --last 5
+octoctl results --since 2h
+octoctl results --since 2026-07-17T12:00:00.000Z --json
+```
+
+The default window is 24 hours. Results returned by the controller have passed
+its identity, ownership, signature, replay, and state checks.
+
+## Interactive and bulk operation
+
+```bash
+octoctl beacon shell --beacon
+octoctl beacon shell --beacon --tentacle notes --timeout 300
+```
+
+The interactive shell uses the authenticated controller HTTP API. It does not
+create an unauthenticated terminal listener.
+
+```bash
+octoctl bulk shell \
+ --beacon-ids , \
+ --cmd "whoami" \
+ --wait \
+ --timeout 120
+```
+
+Bulk shell is elevated and targets every listed beacon. Review the expanded ID
+set before submitting it.
+
+## Build beacon
+
+Simple mode compiles without baking an enrollment identity:
+
+```bash
+octoctl build-beacon --output ./beacon --platform linux-x64
+```
+
+Full mode generates/bakes the beacon ID, X25519 identity, Ed25519 signing
+identity, and non-secret endpoint selection:
+
+```bash
+octoctl build-beacon \
+ --outfile ./beacon-macos-arm64 \
+ --target bun-darwin-arm64 \
+ --tentacle-priority issues,notes
+```
+
+Key options:
+
+| Option | Meaning |
+|---|---|
+| `--beacon-id ` | Use an approved pre-assigned ID instead of generating one |
+| `--target ` | Full-mode Bun compile target; default `bun-linux-x64` |
+| `--relay ` | Add a relay consortium entry; repeatable |
+| `--grpc-url ` | Bake a direct TLS gRPC endpoint |
+| `--http-url ` | Bake the direct HTTPS endpoint |
+| `--codespace-name`, `--github-user` | Bake non-secret Codespaces discovery fields |
+| `--tentacle-priority ` | Bake priority order |
+| `--no-random-title` | Use the default Issues title rather than a randomized title |
+
+Runtime PATs, bearer credentials, client private keys, and App private keys are
+not build options. Supply them securely on the target.
+
+## Recovery records
+
+```bash
+octoctl drop create \
+ --beacon \
+ --configuration-file ./recovery-config.json \
+ --generation 2 \
+ --recovery-signing-secret-file /secure/recovery-signing.key
+
+octoctl drop list --beacon
+```
+
+Repository coordinates, ref, writer token, signing public key/key ID, issuance,
+expiry, and data directory have explicit flags or documented environment
+fallbacks. Follow [Recovery](RECOVERY.md); a hand-authored partial record will
+be rejected.
+
+## Proxy routes
+
+```bash
+octoctl proxy create \
+ --decoy-repo \
+ --beacon \
+ --ctrl-owner \
+ --ctrl-repo \
+ --proxy-installation-id
+
+octoctl proxy templates
+octoctl proxy list
+octoctl proxy rotate ''
+```
+
+`proxy create` can add `--create-repo` and `--scaffold`. Dispatch tokens and the
+stable relay signing key should come from protected environment variables. See
+the [proxy workflow contract](../templates/proxy/README.md).
+
+## Local service commands
+
+```bash
+octoctl start --env .env
+octoctl start server --env .env
+octoctl status
+octoctl stop dashboard
+octoctl stop
+octoctl update --branch main
+```
+
+`start` manages local background processes; it is not a production service
+manager. `update` pulls the named branch and reinstalls dependencies, so review
+local changes and upstream changes before using it.
+
+## Output and exit behavior
+
+Use `--json` where offered for automation. Successful commands exit `0`.
+Validation, configuration, API, and filesystem failures exit non-zero and emit
+an actionable message. Never parse human-oriented tables when JSON output is
+available.
diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md
new file mode 100644
index 0000000..5fe5727
--- /dev/null
+++ b/docs/CONFIGURATION.md
@@ -0,0 +1,247 @@
+# Configuration Reference
+
+OctoC2 reads configuration from environment variables, protected files, and
+pre-enrollment defines created by `octoctl build-beacon`. This page covers the
+operator-facing variables; [Operations and assurance](PRODUCTION.md) and
+[Recovery](RECOVERY.md) define their security invariants in depth.
+
+> [!IMPORTANT]
+> **Authorized use only.** Keep every configured repository, identity, and
+> listener within the approved environment and keep secrets out of source.
+
+## Loading and precedence
+
+- `octoctl start --env ` loads the selected dotenv file for locally
+ managed components.
+- Directly started Bun processes inherit the shell environment.
+- Explicit CLI options override their documented environment fallback.
+- Full beacon builds bake identity and selected non-secret routing fields.
+- Signed recovery configuration can replace the recovery-controlled runtime
+ fields only when its complete policy, signature, recipient, generation, and
+ expiry validate.
+
+Never commit `.env`, PEM files, bearer-token maps, PATs, enrollment artifacts,
+or controller state.
+
+## Minimal controller
+
+| Variable | Required | Default | Purpose |
+|---|---:|---|---|
+| `OCTOC2_SERVER_GITHUB_TOKEN` | Yes | — | Server GitHub API credential |
+| `OCTOC2_REPO_OWNER` | Yes | — | Control repository owner |
+| `OCTOC2_REPO_NAME` | Yes | — | Control repository name |
+| `OCTOC2_OPERATOR_SECRET` | Yes | — | Base64url 32-byte X25519 secret |
+| `OCTOC2_POLL_INTERVAL_MS` | No | `30000` | GitHub channel polling interval |
+| `OCTOC2_DATA_DIR` | No | `./data` | SQLite and controller state directory |
+| `OCTOC2_SERVER_GIST_TOKEN` | For Gist | — | Dedicated controller Gist PAT |
+| `OCTOC2_ENROLLMENT_DIR` | Recommended | — | Directory of pre-enrollment artifacts imported at startup |
+
+The controller resolves the operator public key from the
+`MONITORING_PUBKEY` repository variable and checks it against the configured
+operator secret.
+
+## HTTPS and operator API
+
+| Variable | Required when enabled | Default | Purpose |
+|---|---:|---|---|
+| `OCTOC2_HTTP_ENABLED` | — | `false` | Enable HTTPS/WSS operator and beacon API |
+| `OCTOC2_HTTP_HOST` | No | `127.0.0.1` | Bind host |
+| `OCTOC2_HTTP_PORT` | No | `8080` | Bind port |
+| `OCTOC2_HTTP_SERVER_CERT` | Yes | — | Server certificate chain file |
+| `OCTOC2_HTTP_SERVER_KEY` | Yes | — | Server private-key file |
+| `OCTOC2_HTTP_CA_CERT` | Client-side | — | CA trust file used by CLI/development tooling |
+| `OCTOC2_OPERATOR_API_TOKEN` | Yes | — | Operator-only bearer credential |
+| `OCTOC2_BEACON_API_TOKENS` | Yes | — | JSON map of exact beacon ID to unique bearer token |
+| `OCTOC2_DASHBOARD_ORIGIN` | No | local policy | Allowed dashboard origin |
+
+Ports must be integers from 1 through 65535. Non-loopback binds produce an
+exposure warning and require an explicitly reviewed network boundary.
+
+Example credential map:
+
+```json
+{"2f10b98a-0000-4000-8000-000000000001":"replace-with-a-unique-random-token"}
+```
+
+The GitHub token, operator API token, and every beacon token must be distinct.
+
+## gRPC
+
+| Variable | Required when enabled | Default | Purpose |
+|---|---:|---|---|
+| `OCTOC2_GRPC_ENABLED` | — | `false` | Enable direct/Codespaces/relay gRPC |
+| `OCTOC2_GRPC_HOST` | No | `127.0.0.1` | Bind host |
+| `OCTOC2_GRPC_PORT` | No | `50051` | Bind port |
+| `OCTOC2_GRPC_CA_CERT` | Yes | — | CA used to verify client certificates |
+| `OCTOC2_GRPC_SERVER_CERT` | Yes | — | Server certificate chain |
+| `OCTOC2_GRPC_SERVER_KEY` | Yes | — | Server private key |
+| `OCTOC2_GRPC_CLIENT_CERT_FINGERPRINTS` | Yes | — | JSON map of beacon ID to exact SHA-256 client-cert fingerprint |
+| `OCTOC2_BEACON_API_TOKENS` | Yes | — | Per-beacon bearer map, also required for gRPC |
+
+Each beacon needs a distinct client certificate. A shared certificate or
+wildcard fingerprint map is rejected by policy.
+
+## Lifecycle and replay windows
+
+| Variable | Default | Constraint or meaning |
+|---|---:|---|
+| `OCTOC2_LIFECYCLE_INTERVAL_MS` | `60000` | Sweep interval |
+| `OCTOC2_BEACON_DORMANT_AFTER_MS` | `600000` | Mark a silent beacon dormant after 10 minutes |
+| `OCTOC2_BEACON_LOST_AFTER_MS` | `86400000` | Mark it lost after 24 hours; must exceed dormant threshold |
+| `OCTOC2_PROCESSED_MESSAGE_RETENTION_MS` | `2592000000` | Retain replay records for 30 days |
+| `OCTOC2_CHECKIN_MAX_AGE_MS` | `1800000` | Reject check-ins older than 30 minutes |
+| `OCTOC2_CHECKIN_MAX_FUTURE_SKEW_MS` | `300000` | Allow at most five minutes future clock skew |
+
+## OIDC
+
+Set `OCTOC2_OIDC_BINDINGS` to an array of exact bindings:
+
+```json
+[
+ {
+ "repository": "Owner/Repo",
+ "beaconId": "2f10b98a-0000-4000-8000-000000000001",
+ "subjects": ["repo:Owner/Repo:environment:prod"],
+ "workflowRefs": ["Owner/Repo/.github/workflows/transport.yml@refs/heads/main"]
+ }
+]
+```
+
+`OCTOC2_OIDC_AUDIENCE` optionally replaces the default audience. Repository,
+beacon ID, subject, and workflow ref are exact; wildcards and empty arrays are
+not accepted.
+
+## Recovery publisher
+
+Set `OCTOC2_RECOVERY_PUBLISH_ENABLED=true`, then configure:
+
+- `OCTOC2_GITHUB_APP_ID`
+- `OCTOC2_GITHUB_APP_PRIVATE_KEY_FILE`
+- `OCTOC2_GITHUB_APP_POLICIES`
+- `OCTOC2_RECOVERY_REPO_OWNER`, `OCTOC2_RECOVERY_REPO_NAME`, and optional
+ `OCTOC2_RECOVERY_REPO_REF` (default `main`)
+- `OCTOC2_RECOVERY_WRITE_TOKEN`
+- `OCTOC2_RECOVERY_SIGNING_SECRET_FILE`
+- `OCTOC2_RECOVERY_SIGNING_PUBLIC_KEY` and optional derived
+ `OCTOC2_RECOVERY_SIGNING_KEY_ID`
+- `OCTOC2_RECOVERY_POLICIES`
+- optional `OCTOC2_RECOVERY_PUBLISH_INTERVAL_MS`
+- optional paired `OCTOC2_RECOVERY_NEXT_SIGNING_PUBLIC_KEY` and
+ `OCTOC2_RECOVERY_NEXT_SIGNING_KEY_ID` during key rotation
+
+App and recovery policies must name the same exact beacon IDs. Policy formats
+are documented in [Recovery](RECOVERY.md).
+
+## Beacon runtime
+
+### Identity and repository
+
+| Variable | Required | Purpose |
+|---|---:|---|
+| `OCTOC2_REPO_OWNER`, `OCTOC2_REPO_NAME` | Yes | Current control repository |
+| `SVC_GITHUB_TOKEN` | One bootstrap path | Explicit scoped repository credential |
+| `SVC_GITHUB_TOKEN_LEASE` | One bootstrap path | Server-issued, repository- and beacon-bound lease JSON |
+| `SVC_BEACON_API_TOKEN` | Direct transports | Beacon's unique controller bearer credential |
+| `SVC_GIST_TOKEN` | Gist | Dedicated beacon Gist PAT |
+| `OCTOC2_OPERATOR_PUBKEY` | Without GitHub key lookup | Provisioned 32-byte X25519 public key |
+
+Configure either `SVC_GITHUB_TOKEN` or `SVC_GITHUB_TOKEN_LEASE`, never both.
+App private-key variables are rejected on a beacon. Full builds provision the
+beacon ID, X25519 pair, and Ed25519 signing identity; hand-setting those defines
+is discouraged.
+
+### Selection and timing
+
+| Variable | Default | Constraint or purpose |
+|---|---:|---|
+| `SVC_TENTACLE_PRIORITY` | Auto-detect direct gRPC, HTTP, then Issues | Comma-separated selectable channel kinds |
+| `SVC_SLEEP` | `60` | Integer seconds, 1 through 86400 |
+| `SVC_JITTER` | `0.3` | Number from 0 through 1 |
+| `SVC_CLEANUP_DAYS` | disabled | Result-comment retention; `0` means immediate cleanup |
+| `SVC_RECOVERY_POLL_INTERVAL_MS` | `60000` | 10000 through 2700000 |
+| `OCTOC2_LOG_LEVEL` | `info` | `debug`, `info`, `warn`, or `error` |
+| `OCTOC2_STATE_DIR` | platform state path | Override persistent beacon state directory |
+
+Invalid entries in an explicit priority list are logged and ignored; if no
+valid entry remains, Issues is selected. Review the startup log instead of
+assuming a misspelled channel was selected.
+
+### HTTP, gRPC, and Codespaces
+
+| Variable | Purpose |
+|---|---|
+| `SVC_HTTP_URL` | Trusted HTTPS controller origin |
+| `SVC_GRPC_DIRECT` | Direct TLS gRPC `host:port` |
+| `SVC_GRPC_CA_CERT` | Trusted server CA file |
+| `SVC_GRPC_CLIENT_CERT`, `SVC_GRPC_CLIENT_KEY` | Beacon-specific mTLS identity |
+| `SVC_GRPC_CODESPACE_NAME` | Existing approved Codespace name |
+| `SVC_GITHUB_USER` | GitHub username for Codespace SSH |
+| `SVC_CODESPACES_GITHUB_TOKEN` | Dedicated user-scoped Codespaces PAT |
+| `SVC_AUTO_PROVISION_CODESPACE` | Explicit `true`/`1` opt-in to create/start a Codespace |
+| `SVC_GRPC_PORT` | Remote gRPC port, default `50051` |
+| `SVC_GRPC_LOCAL_PORT` | Local SSH-forward port, default `50051` |
+
+Do not point `SVC_GRPC_DIRECT` at a GitHub Dev Tunnels HTTPS URL; use the SSH
+tunnel path for gRPC or `SVC_HTTP_URL` for HTTPS.
+
+### Issues acknowledgement tuning
+
+| Variable | Default |
+|---|---:|
+| `SVC_POLL_TIMEOUT_MS` | `30000` |
+| `SVC_POLL_RETRY_MS` | `10000` |
+| `SVC_RESULT_ACK_TIMEOUT_MS` | `120000` |
+| `SVC_RESULT_ACK_RETRY_MS` | `5000` |
+| `SVC_ISSUE_TITLE` | Generated/default title policy |
+
+Increase acknowledgement windows only to accommodate measured GitHub or proxy
+latency. A longer timeout does not fix missing permissions or a mismatched key.
+
+### Recovery bootstrap
+
+The beacon requires these source fields together:
+
+- `OCTOC2_RECOVERY_REPO_OWNER`
+- `OCTOC2_RECOVERY_REPO_NAME`
+- `OCTOC2_RECOVERY_REPO_REF`
+
+It also requires these trust fields together:
+
+- `OCTOC2_RECOVERY_SIGNING_PUBLIC_KEY`
+- `OCTOC2_RECOVERY_SIGNING_KEY_ID`
+
+Source and trust groups must both be present. `SVC_PROXY_REPOS` is retired;
+proxy routes and their short-lived credentials must arrive through signed
+recovery configuration.
+
+## Dashboard
+
+| Variable | Purpose |
+|---|---|
+| `VITE_C2_SERVER_URL` | Bare HTTPS controller origin; no path, query, fragment, or userinfo |
+| `VITE_GITHUB_OWNER`, `VITE_GITHUB_REPO` | Direct GitHub fallback repository |
+
+Dashboard credentials are entered at login and held in React memory only. The
+development proxy verifies controller TLS.
+
+## Placeholder-only dotenv example
+
+```dotenv
+OCTOC2_SERVER_GITHUB_TOKEN=
+OCTOC2_REPO_OWNER=
+OCTOC2_REPO_NAME=
+OCTOC2_OPERATOR_SECRET=
+OCTOC2_DATA_DIR=
+
+OCTOC2_HTTP_ENABLED=true
+OCTOC2_HTTP_HOST=127.0.0.1
+OCTOC2_HTTP_PORT=8080
+OCTOC2_HTTP_SERVER_CERT=
+OCTOC2_HTTP_SERVER_KEY=
+OCTOC2_HTTP_CA_CERT=
+OCTOC2_OPERATOR_API_TOKEN=
+OCTOC2_BEACON_API_TOKENS={"":""}
+```
+
+Keep beacon runtime configuration in the target's protected environment, not
+the controller dotenv when the target is a different machine.
diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md
new file mode 100644
index 0000000..e8cf9a8
--- /dev/null
+++ b/docs/DEVELOPMENT.md
@@ -0,0 +1,152 @@
+# Development Guide
+
+OctoC2 is a Bun/TypeScript monorepo with strict shared contracts. Behavior
+changes should update the canonical catalog or schema first, then every
+producer, consumer, test, and guide affected by that contract.
+
+> [!IMPORTANT]
+> **Authorized use only.** Keep development fixtures isolated and never commit
+> live credentials, private-repository content, host data, or personal data.
+
+## Workspaces
+
+| Workspace | Entry point | Test command |
+|---|---|---|
+| `shared/` | library exports | `bun test --timeout 30000` |
+| `implant/` | `src/index.ts` | `bun test --timeout 30000` |
+| `server/` | `src/index.ts` | `bun test --timeout 30000` |
+| `dashboard/` | `src/main.tsx` | `bun test --timeout 30000` |
+| `octoctl/` | `src/index.ts` | `bun test --timeout 30000` |
+| `proxy/` | workflow/runtime sources | `bun test --timeout 30000` |
+| `docs-site/` | `src/main.tsx` | `bun run lint && bun run build` |
+
+Run `bun run typecheck` in each TypeScript workspace. Strict mode and
+`exactOptionalPropertyTypes` are enabled; conditionally spread optional
+properties instead of assigning `undefined`.
+
+## Toolchain and install
+
+Use Bun `1.3.14`, the checked-in lockfile, and the declared Node 22.14 tooling
+range:
+
+```bash
+bun install --frozen-lockfile
+bun run toolchain:check
+bun run deps:check
+```
+
+Do not update one workspace's dependency independently when the package is
+centrally pinned or overridden at the root.
+
+## Repository checks
+
+```bash
+bun run proto:check
+bun run docs:check
+bun run workflows:check
+bun run toolchain:check
+bun run lint
+bun audit
+```
+
+`docs:check` validates consistency-sensitive claims. `workflows:check` verifies
+that Actions dependencies are commit-pinned. `proto:check` ensures generated
+gRPC bindings match their source.
+
+## Protocol changes
+
+Canonical channel definitions live in `shared/src/channels.ts`; task schemas
+and risk classification live in `shared/src/tasks.ts`. Signed envelope and
+identity changes also belong in `shared/`.
+
+When changing a wire contract:
+
+1. Update its canonical shared type and untrusted-input validator.
+2. Add shared positive and negative tests.
+3. Update controller and beacon producers/consumers together.
+4. Preserve version/replay behavior or add an explicit migration.
+5. Regenerate protocol bindings with `bun run proto:gen` if the protobuf changes.
+6. Update CLI/dashboard choices and these guides.
+
+Never make one transport accept a broader payload than the shared contract.
+
+## Adding or changing a task
+
+Update the shared task catalog, its exact argument validator and risk level,
+the implant `TaskExecutor` handler, the server/operator surfaces, and tests for
+unknown fields, range limits, timeouts, and result metadata. Destructive tasks
+must be explicit and must not be selected through a generic fallback.
+
+Remote module loading is deliberately absent. Do not reintroduce arbitrary
+unsigned module execution under a new task alias.
+
+## Adding or changing a channel
+
+1. Add or update the canonical catalog entry and prerequisites.
+2. Implement both implant tentacle and controller counterpart.
+3. Register the implant through `registerTentacles.ts` so initial boot and
+ recovery rebuild remain consistent.
+4. Use the shared token getter for repository App leases.
+5. Define result-acceptance behavior, cursor scope, artifact ownership, replay
+ handling, teardown, and cleanup.
+6. Add unit tests, a local integration path, and an authorized live
+ qualification plan.
+7. Update [Channels](CHANNELS.md), [GitHub setup](GITHUB_SETUP.md), and the
+ traceability record without overstating the evidence level.
+
+Catalog presence is not proof of readiness. Keep incomplete paths selectable
+only when their prerequisites and limitations are explicit; keep missing
+counterparts unavailable.
+
+## Testing patterns
+
+- Use `bun:test`, not Jest or Vitest.
+- Restore environment variables and module mocks after each test.
+- When `mock.module()` arrays are indexed, cast through `as any` if required to
+ avoid TypeScript tuple-index inference errors.
+- Prefer deterministic clocks, IDs, and in-memory or temporary state roots.
+- Test malformed, stale, cross-beacon, replayed, and unauthorized envelopes in
+ addition to the success path.
+- Verify hard-ceiling timeout branches terminate child processes.
+- Preserve the dashboard's isolated test runner to avoid module-mock leakage.
+
+## Builds
+
+```bash
+bun run build:server
+bun run build:dashboard
+bun run build:octoctl
+cd implant && bun run build:all
+```
+
+For a normal behavior change, test the affected target plus its shared
+contracts. Use `bun run smoke:builds` when a change can affect compile targets,
+native dependencies, or runtime startup.
+
+## Documentation standards
+
+- Make the Markdown guides authoritative; keep the docs site as a concise map.
+- Link to implementation status rather than claiming a transport is universally
+ available.
+- Keep local, integration, and live evidence labels distinct.
+- Use placeholder credentials only. Never paste `.env` contents, private repo
+ data, PAT fragments, hostnames, usernames, or live artifact payloads.
+- Link current external setup claims to official GitHub documentation.
+- Run `bun run docs:check` and validate relative links after edits.
+
+## Change checklist
+
+- [ ] Scope is limited to the requested behavior.
+- [ ] No secret, PII, private URL, credential fragment, or generated state is in
+ the diff.
+- [ ] Shared contracts and generated bindings agree.
+- [ ] Changed workspaces pass tests and strict TypeScript checks.
+- [ ] Security-relevant failure cases have regression coverage.
+- [ ] Documentation, CLI help, dashboard choices, and channel/task catalogs
+ agree.
+- [ ] Workflow actions remain commit-pinned.
+- [ ] Live test artifacts are inventoried and cleaned when live testing occurs.
+- [ ] The commit contains no local research notes or unrelated workspace files.
+
+For release-level assurance and stop conditions, use [Operations and
+assurance](PRODUCTION.md) and [Remediation traceability](REMEDIATION_TRACEABILITY.md).
diff --git a/docs/GITHUB_SETUP.md b/docs/GITHUB_SETUP.md
new file mode 100644
index 0000000..5791050
--- /dev/null
+++ b/docs/GITHUB_SETUP.md
@@ -0,0 +1,156 @@
+# GitHub Setup
+
+OctoC2 uses GitHub repositories as explicit transport boundaries. This guide
+defines a least-privilege topology, GitHub App registration, and the separate
+credentials used by the controller, operator, beacon, proxy, and recovery
+publisher.
+
+GitHub changes its registration UI over time. The field guidance below follows
+the current official documentation for [registering a GitHub App](https://docs.github.com/en/apps/creating-github-apps/registering-a-github-app/registering-a-github-app),
+[choosing App permissions](https://docs.github.com/en/apps/creating-github-apps/registering-a-github-app/choosing-permissions-for-a-github-app),
+and [managing private keys](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/managing-private-keys-for-github-apps).
+
+> [!IMPORTANT]
+> **Authorized use only.** Use dedicated test repositories and accounts. Do not grant organization-wide
+> access when selected-repository installation is sufficient.
+
+## Repository topology
+
+| Repository | Visibility | Purpose | Installed App? |
+|---|---|---|---|
+| Control | Private | Primary GitHub-backed task and result transport | Yes |
+| Decoy | Private | Distinct outer repository for the optional proxy route | Yes, when proxy is used |
+| Recovery | Public | Signed and sealed dead-drop records discoverable without a working control credential | No; use a dedicated writer token |
+
+Keep control and decoy separate. A proxy route does not create a useful
+boundary if both names refer to the same repository. The recovery repository
+contains encrypted records and public verification metadata only—never raw
+tokens, private keys, hostnames, usernames, or operator notes.
+
+## Register the GitHub App
+
+Create the App under the account or organization that owns the test
+repositories:
+
+1. Open **Settings → Developer settings → GitHub Apps → New GitHub App**.
+2. Choose a unique, ordinary App name.
+3. Set **Homepage URL** to the project repository or owning account URL.
+4. Leave **Callback URL** empty. OctoC2 does not request a user access token.
+5. Leave **Request user authorization (OAuth) during installation** off.
+6. Leave **Enable Device Flow** off.
+7. Leave **Expire user authorization tokens** checked. It is GitHub's
+ recommended setting and has no effect when user tokens are not requested.
+8. Leave **Setup URL** empty and **Redirect on update** off.
+9. Under **Webhook**, clear **Active**. OctoC2 polls authenticated APIs and does
+ not require a webhook URL, secret, or event subscription.
+10. Select **Only on this account** unless an approved deployment requires the
+ App to be installable elsewhere.
+
+### Repository permissions
+
+Always retain **Metadata: read-only**. Add only the permissions required by the
+channels you enable:
+
+| Channel | Repository permission |
+|---|---|
+| Issues, proxy inner/outer exchange | Issues: read and write |
+| Branch, Git Notes, steganography | Contents: read and write |
+| Actions | Actions: read and write; Variables: read and write |
+| Secrets (`7b`) | Variables: read and write |
+| Pages | Deployments: read and write |
+
+Gists and Codespaces are user-level surfaces and do not use the repository App
+installation token. OIDC authenticates with GitHub Actions identity tokens.
+Direct HTTP and gRPC use controller credentials and certificates rather than
+GitHub repository permissions. The reserved `pull_request` catalog entry is
+not selectable.
+
+### Install and record the App identity
+
+After creating the App:
+
+1. Note its numeric **App ID**.
+2. Generate a private key and save the downloaded PEM on the controller only.
+3. Restrict the file to the controller service account (`chmod 600` on Unix).
+4. Install the App using **Only select repositories** and select the control
+ repository plus the decoy repository if proxy is enabled.
+5. Record the numeric installation ID from the installation settings URL or
+ query it through the GitHub API.
+
+The controller exchanges the App JWT for short-lived, repository-restricted
+[installation access tokens](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-an-installation-access-token-for-a-github-app).
+The App private key never belongs on a beacon, in an Actions secret, or in a
+recovery record.
+
+## Credential roles
+
+No single PAT needs access to every surface. Create one credential per role and
+scope it to the named account and repository.
+
+| Role | Account/repository placement | Minimum access |
+|---|---|---|
+| Controller repository token | Dedicated controller identity; control repo only | Metadata read plus enabled channel permissions from the table above |
+| Operator GitHub token | Operator account; control repo only | Issues read/write and Variables read/write when using direct GitHub dashboard/CLI paths and `MONITORING_PUBKEY` |
+| Beacon Gist token | Dedicated Gist account | Classic PAT with `gist` |
+| Controller Gist token | Same dedicated Gist account, different token | Classic PAT with `gist` |
+| Codespaces runtime token | Account that owns or can access the named Codespace | Classic PAT with `codespace`; do not reuse a repository token |
+| Recovery writer | Recovery owner; recovery repo only | Fine-grained Contents read/write (Metadata is implicit) |
+| Proxy dispatch token | Exact workflow target repo | Fine-grained Contents write as required for repository dispatch |
+
+GitHub recommends fine-grained PATs when they support the required operation;
+see [managing personal access tokens](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens).
+Gist and current Codespaces CLI/API paths require their noted user-scoped
+classic PATs. A third GitHub account is not inherently required: separation is
+by credential role, though a dedicated service account reduces the blast
+radius and makes audit attribution clearer.
+
+OctoC2 application credentials are not PATs:
+
+| Credential | Purpose |
+|---|---|
+| `OCTOC2_OPERATOR_API_TOKEN` | Dashboard/CLI authentication to the controller |
+| `OCTOC2_BEACON_API_TOKENS` | Exact beacon ID to bearer-token map for direct transports |
+| `OCTOC2_OPERATOR_SECRET` | X25519 secret used to decrypt results and seal tasks |
+| Beacon Ed25519 key | Persistent signing identity for check-ins and results |
+| Recovery Ed25519 key | Server-only signing identity for dead-drop records |
+
+## Repository variables and secrets
+
+Set `MONITORING_PUBKEY` as an Actions repository variable on every repository
+that directly carries encrypted tasks. Its value is the operator's X25519
+public key generated by `octoctl keygen`; it is not a secret.
+
+Proxy workflow repositories use the variables and secrets documented in the
+[proxy workflow contract](../templates/proxy/README.md). Keep workflow dispatch
+tokens in Actions secrets, bind route variables to exact repositories, and do
+not reuse the controller or recovery-writer token.
+
+## Validate the setup
+
+Before starting a beacon:
+
+- Confirm the App installation lists only the intended control and decoy repos.
+- Compare granted permissions with the enabled channel set.
+- Confirm the control and decoy repositories are private.
+- Confirm the recovery repository contains no plaintext secrets or personal
+ data.
+- Verify `MONITORING_PUBKEY` matches the operator secret you will load.
+- Verify controller and beacon Gist tokens differ.
+- Verify the Codespaces PAT belongs to the account that can open the named
+ Codespace.
+- Keep every private key and PAT out of Git, logs, screenshots, and compiled
+ binaries.
+
+Run `octoctl setup --phase validate` for the CLI's configured checks, then
+continue with [configuration](CONFIGURATION.md) and the [quickstart](QUICKSTART.md).
+
+## Rotation
+
+Rotate one role at a time. Update the consuming component, restart it, and
+verify a harmless task before revoking the replaced credential. GitHub App
+private-key rotation can overlap keys; installation tokens are short-lived and
+must be allowed to expire. Recovery signing-key rotation requires a signed key
+transition and is covered in [Recovery](RECOVERY.md).
+
+Retain or revoke test PATs according to the operator's explicit cleanup plan.
+Repository artifact cleanup does not imply credential deletion.
diff --git a/docs/INSTALLATION.md b/docs/INSTALLATION.md
new file mode 100644
index 0000000..816cad8
--- /dev/null
+++ b/docs/INSTALLATION.md
@@ -0,0 +1,173 @@
+# Installation
+
+This guide installs the OctoC2 controller, dashboard, CLI, and beacon build
+tooling from source. Complete [GitHub setup](GITHUB_SETUP.md) before attempting
+an end-to-end run.
+
+> [!IMPORTANT]
+> **Authorized use only.** Use OctoC2 only on systems and repositories you own or are explicitly
+> authorized to test. Keep test repositories private and listener surfaces
+> private unless an approved test requires otherwise.
+
+## Requirements
+
+| Requirement | Version or purpose |
+|---|---|
+| [Bun](https://bun.sh/docs/installation) | `1.3.14` (the pinned package manager and runtime) |
+| Git | Clone, update, and Git Notes transport operations |
+| Node.js | `22.14.x` only for tooling that explicitly invokes Node |
+| GitHub CLI | Required only for Codespaces discovery and SSH tunneling |
+
+Use the exact Bun version. The lockfile, CI, native tests, compiled binaries,
+and dependency-policy checks are qualified against that version.
+
+## Install the repository
+
+```bash
+git clone https://github.com/dstours/OctoC2.git
+cd OctoC2
+bun install --frozen-lockfile
+bun run proto:gen
+bun run deps:check
+bun run docs:check
+```
+
+`--frozen-lockfile` prevents an install from silently changing the dependency
+graph. `proto:gen` produces the checked-in protocol bindings used by gRPC.
+
+## Run components from source
+
+Open separate terminals from the repository root:
+
+```bash
+# Controller
+cd server
+bun run src/index.ts
+```
+
+```bash
+# Dashboard
+cd dashboard
+bun run dev
+```
+
+```bash
+# CLI
+cd octoctl
+bun run src/index.ts --help
+```
+
+The dashboard development server listens on `http://127.0.0.1:5173`. The
+controller's HTTP and gRPC listeners remain disabled unless explicitly
+configured.
+
+For a shell-friendly CLI command during development, define an alias:
+
+```bash
+alias octoctl='bun run --cwd=/absolute/path/to/OctoC2/octoctl src/index.ts'
+octoctl --help
+```
+
+On PowerShell, use a function in the current session:
+
+```powershell
+function octoctl { bun run --cwd C:\tools\OctoC2\octoctl src/index.ts @args }
+octoctl --help
+```
+
+## Build standalone components
+
+```bash
+bun run build:server
+bun run build:dashboard
+bun run build:octoctl
+```
+
+Default outputs are written to each workspace's `dist/` directory. The server
+and CLI package scripts currently target Linux x64. Run the CLI from source on
+other operator platforms unless you intentionally change and verify its Bun
+compile target.
+
+### Build a beacon
+
+The implant workspace provides the supported compile targets:
+
+| Target | Command | Output |
+|---|---|---|
+| Linux x64 | `bun run build:linux-x64` | `dist/beacon-linux-x64` |
+| Linux arm64 | `bun run build:linux-arm64` | `dist/beacon-linux-arm64` |
+| Windows x64 | `bun run build:windows-x64` | `dist/beacon-windows-x64.exe` |
+| macOS Apple silicon | `bun run build:darwin-arm64` | `dist/beacon-macos-arm64` |
+| macOS Intel | `bun run build:darwin-x64` | `dist/beacon-macos-x64` |
+
+```bash
+cd implant
+bun run build:darwin-arm64
+```
+
+The compiled beacon contains the Bun runtime; the target VM does not need Bun.
+Transfer it through an approved path, make it executable on Unix-like systems,
+and supply runtime secrets through the VM environment:
+
+```bash
+chmod 700 ./beacon-macos-arm64
+./beacon-macos-arm64
+```
+
+For a pre-enrolled build with a generated identity and explicit transport
+settings, use `octoctl build-beacon`. See [CLI reference](CLI.md#build-beacon)
+and never bake PATs, bearer tokens, private App keys, or recovery signing keys
+into the binary.
+
+## Directory map
+
+| Path | Purpose |
+|---|---|
+| `implant/` | Beacon runtime and platform builds |
+| `server/` | Controller, durable state, GitHub channel pollers, HTTP, and gRPC |
+| `dashboard/` | Local operator web interface |
+| `octoctl/` | Operator CLI and provisioning workflows |
+| `shared/` | Canonical task, channel, envelope, and identity contracts |
+| `proxy/` | Relay workflow implementation and tests |
+| `templates/proxy/` | Files provisioned into control and decoy repositories |
+| `docs-site/` | Public documentation landing site |
+
+## Verify the installation
+
+Run the repository-level policy checks first:
+
+```bash
+bun run deps:check
+bun run docs:check
+bun run workflows:check
+bun run toolchain:check
+bun run proto:check
+```
+
+Then run workspace tests and type checks from each changed workspace:
+
+```bash
+cd shared && bun test --timeout 30000 && bun run typecheck
+cd ../implant && bun test --timeout 30000 && bun run typecheck
+cd ../server && bun test --timeout 30000 && bun run typecheck
+cd ../dashboard && bun test --timeout 30000 && bun run typecheck
+cd ../octoctl && bun test --timeout 30000 && bun run typecheck
+```
+
+Continue with the [quickstart](QUICKSTART.md) to configure credentials, enroll a
+beacon, and verify a harmless `ping` task.
+
+## Update an installation
+
+Stop running components, preserve the controller data directory, and then:
+
+```bash
+git pull --ff-only
+bun install --frozen-lockfile
+bun run proto:check
+bun run docs:check
+```
+
+Review release changes before rebuilding beacons. Do not replace an enrolled
+beacon identity, controller database, or recovery generation as an incidental
+part of a source update.
diff --git a/docs/QUICKSTART.md b/docs/QUICKSTART.md
index 18e4581..e140698 100644
--- a/docs/QUICKSTART.md
+++ b/docs/QUICKSTART.md
@@ -1,207 +1,234 @@
-# Local Evaluation Quickstart
+# Quickstart
+
+This quickstart takes a fresh checkout to a pre-enrolled beacon and an accepted
+`ping` result through a private GitHub Issues channel. It keeps direct network
+listeners disabled for the first run.
> [!IMPORTANT]
-> **Authorized use only.** Use a private repository and scoped credentials,
-> keep listeners on loopback or a reviewed private network, and test only
-> systems and repositories you have explicit permission to access.
+> **Authorized use only.** Use an isolated private repository and only deploy the beacon to a system you
+> own or are explicitly authorized to test.
-## 1. Install and verify the toolchain
+## Before you start
-Use Bun `1.3.14`. The repository pins Node.js `22.14.0` for tools that
-explicitly require Node.
+Complete these once:
-```bash
-bun install --frozen-lockfile
-bun run proto:gen
-```
+1. [Install](INSTALLATION.md) Bun `1.3.14` and the repository dependencies.
+2. Create a private control repository and configure the least-privilege
+ credentials in [GitHub setup](GITHUB_SETUP.md).
+3. Decide on a protected local directory for controller state, enrollment
+ artifacts, and secrets.
+
+For the Issues baseline you need:
+
+- a controller GitHub credential scoped to the control repo with Metadata read
+ and Issues read/write;
+- an independently scoped beacon GitHub credential or a valid narrowed App
+ installation-token lease;
+- an operator X25519 keypair;
+- the same private owner/repo coordinates on controller and beacon.
+
+## Guided setup
-Run the workspace checks before starting any component:
+The wizard is the recommended first-run path:
```bash
-bun run deps:check
-bun run docs:check
-bun run workflows:check
-bun run toolchain:check
-bun run proto:check
-bun run lint
-bun audit
-cd shared && bun test --timeout 30000 && bun run typecheck
-cd ../implant && bun test --timeout 30000 && bun run typecheck
-cd ../server && bun test --timeout 30000 && bun run typecheck
-cd ../dashboard && bun run test && bun run build
-cd ../octoctl && bun test --timeout 30000 && bun run typecheck
-cd ../proxy && bun run typecheck && bun run build
-cd ../docs-site && bun run lint && bun run build
-cd ../implant && bun run build:all
-cd ../server && bun run build
-cd ../octoctl && bun run build
-cd ../proxy && bun run build
-cd .. && bun run smoke:builds
+cd OctoC2
+bun run octoctl/src/index.ts setup
```
-## 2. Create role-separated test credentials
+It walks through credentials, validation, key generation, authentication,
+channel selection, environment generation, pre-enrolled beacon compilation,
+CLI installation, and verification. Review every proposed repository and
+credential role before accepting it.
-Do not reuse credentials across these roles:
+You can revisit one phase later:
-1. Controller-to-GitHub credential: `OCTOC2_SERVER_GITHUB_TOKEN`
-2. Optional Gist-controller credential: `OCTOC2_SERVER_GIST_TOKEN`
-3. Operator REST/SSE credential: `OCTOC2_OPERATOR_API_TOKEN`
-4. Per-beacon HTTP/gRPC credentials: `OCTOC2_BEACON_API_TOKENS`
-5. Optional direct-dashboard GitHub PAT
-
-The operator token must not appear in the beacon token map. None of the
-controller API credentials may equal the GitHub credential.
+```bash
+bun run octoctl/src/index.ts setup --phase validate
+```
-When Gist is enabled, use two distinct Gist-capable tokens belonging to the
-same dedicated GitHub account: the controller token above and a beacon runtime
-token supplied as `SVC_GIST_TOKEN`. The Gist controller token must not equal the repository, operator API,
-or beacon API credentials.
+The remainder of this guide shows the equivalent lifecycle so you can verify
+the wizard's output or configure manually.
-Generate the operator encryption keypair with the CLI:
+## 1. Generate the operator key
```bash
cd octoctl
bun run src/index.ts keygen
```
-Keep the secret key out of the repository.
+Store the secret as `OCTOC2_OPERATOR_SECRET` in the protected controller
+environment. Set the public value as the control repository Actions variable
+`MONITORING_PUBKEY`, or let the CLI do it with a direct operator token:
+
+```bash
+bun run src/index.ts keygen --set-variable
+```
+
+Do not regenerate this key between build and first run.
-## 3. Start the controller without listeners
+## 2. Build and enroll a beacon
-Set the required GitHub repository and operator-key variables, then run:
+From the repository root, with `OCTOC2_REPO_OWNER` and
+`OCTOC2_REPO_NAME` set:
```bash
-cd server
-bun run src/index.ts
+bun run octoctl/src/index.ts build-beacon \
+ --outfile ./out/beacon \
+ --target bun-darwin-arm64 \
+ --tentacle-priority issues
```
-HTTP and gRPC remain disabled unless their `*_ENABLED` variables are explicitly
-set.
+Choose the target that matches the authorized endpoint:
-For a loopback-only HTTP evaluation, add:
+- `bun-linux-x64`
+- `bun-linux-arm64`
+- `bun-windows-x64`
+- `bun-darwin-arm64`
+- `bun-darwin-x64`
-```text
-OCTOC2_HTTP_ENABLED=true
-OCTOC2_HTTP_HOST=127.0.0.1
-OCTOC2_HTTP_PORT=8080
-OCTOC2_HTTP_SERVER_CERT=/absolute/path/to/http-server.crt
-OCTOC2_HTTP_SERVER_KEY=/absolute/path/to/http-server.key
-# For an internal CA, octoctl also reads:
-OCTOC2_HTTP_CA_CERT=/absolute/path/to/http-ca.crt
-OCTOC2_OPERATOR_API_TOKEN=
-OCTOC2_BEACON_API_TOKENS={"":""}
-OCTOC2_SERVER_URL=https://localhost:8080
+Full mode writes both the binary and a public enrollment artifact named
+`.enrollment.json`. The artifact contains public enrollment material,
+not the beacon's runtime PAT, but still belongs in the protected operator
+workflow.
+
+Set `OCTOC2_ENROLLMENT_DIR` on the controller to the directory containing that
+artifact. The controller must import it before the beacon connects.
+
+## 3. Configure the controller
+
+Create a protected dotenv file outside Git tracking:
+
+```dotenv
+OCTOC2_SERVER_GITHUB_TOKEN=
+OCTOC2_REPO_OWNER=
+OCTOC2_REPO_NAME=
+OCTOC2_OPERATOR_SECRET=
+OCTOC2_DATA_DIR=
+OCTOC2_ENROLLMENT_DIR=
```
-The HTTP certificate SAN must contain the hostname in `OCTOC2_SERVER_URL`.
-Install the issuing CA in the browser and beacon host trust stores. Do not
-disable certificate verification; use a publicly trusted certificate or a
-locally trusted test CA.
+The repository credential, operator secret, and beacon credential are
+different roles. Do not place the controller token in the beacon environment.
-For gRPC, also set `OCTOC2_GRPC_ENABLED=true`, keep
-`OCTOC2_GRPC_HOST=127.0.0.1`, provide per-beacon credentials, and configure the
-required CA/server certificate and key files. Bind each beacon ID to its
-client certificate fingerprint:
+Start the controller:
-```text
-OCTOC2_GRPC_CLIENT_CERT_FINGERPRINTS={"":""}
+```bash
+bun run octoctl/src/index.ts start server --env /secure/path/octoc2.env
```
-The server uses `OCTOC2_GRPC_CA_CERT`, `OCTOC2_GRPC_SERVER_CERT`, and
-`OCTOC2_GRPC_SERVER_KEY`. Each beacon uses `SVC_GRPC_CA_CERT`,
-`SVC_GRPC_CLIENT_CERT`, `SVC_GRPC_CLIENT_KEY`, and the matching
-`SVC_BEACON_API_TOKEN`.
+Or run it in the foreground after loading the same environment:
-Obtain the fingerprint with
-`openssl x509 -in beacon-client.crt -noout -fingerprint -sha256`. The
-fingerprint map must have exactly the same keys as
-`OCTOC2_BEACON_API_TOKENS`, and every fingerprint must be unique. The
-controller refuses to start on a missing, extra, malformed, or reused binding.
-Do not expose plaintext or unauthenticated gRPC.
+```bash
+cd server
+bun run src/index.ts
+```
+
+Confirm the startup log reports one imported enrollment artifact, the expected
+owner/repo, and disabled HTTP/gRPC listeners. The Issues, Branch, Actions,
+Secrets, Pages, Stego, and Notes controller pollers start against the configured
+repo; the beacon priority determines which channel it uses.
-Direct gRPC requires no GitHub credential. Codespaces API/SSH mode also
-requires a protected runtime `SVC_CODESPACES_GITHUB_TOKEN` with the necessary
-user-level Codespaces access: use a classic PAT with only the `codespace` scope,
-because fine-grained PATs do not supply GitHub CLI tunnel metadata. Do not use an App installation lease for that
-user-level API, and do not bake this token into the beacon. Install GitHub CLI
-(`gh`) on the beacon host; Codespaces forwarding uses its authenticated
-connection service and never treats the PAT as an SSH password.
+## 4. Start the beacon
-## 4. Start the local dashboard
+Transfer the compiled binary to the authorized endpoint through an approved
+path. Supply only its runtime role configuration:
```bash
-cd dashboard
-bun run dev
+export SVC_GITHUB_TOKEN=''
+export OCTOC2_REPO_OWNER=''
+export OCTOC2_REPO_NAME=''
+export SVC_TENTACLE_PRIORITY='issues'
+chmod 700 ./beacon
+./beacon
```
-Open `http://127.0.0.1:5173`.
+Use a protected service environment on Windows rather than shell `export`.
+Never pass a PAT as a command-line argument. The first successful check-in must
+match the pre-enrolled ID and signing public key.
-- Live mode uses only the operator API token for controller HTTPS/SSE calls.
-- Direct GitHub mode uses only the GitHub PAT.
-- The optional operator private key is used for result decryption.
-- All three values stay in memory and are cleared on logout or tab close.
+## 5. Verify the first task/result
-## 5. Explore the operator CLI
+List the beacon from controller state:
```bash
-cd octoctl
-bun run src/index.ts --help
+bun run octoctl/src/index.ts beacons
+```
+
+Queue a harmless task:
+
+```bash
+bun run octoctl/src/index.ts task --kind ping --tentacle issues
```
-The CLI displays an authorization reminder at startup. Unsigned remote modules
-are outside the supported surface: the `module` command is absent and
-`task --kind load-module` is rejected.
+Then retrieve the accepted result:
-## 6. Interpret capability status correctly
+```bash
+bun run octoctl/src/index.ts results --last 1 --json
+```
-The repository includes multiple channel implementations. The dashboard’s
-channel grid reports recent observations from beacons; it is not a readiness
-matrix and does not prove every implementation works end to end.
+The run is complete when all of these are observed for the same task ID:
-Before describing any capability as verified, record:
+1. controller queues a validated task for the enrolled beacon;
+2. encrypted task comment is published;
+3. beacon receives and executes `ping` once;
+4. beacon publishes a signed and encrypted result;
+5. controller verifies and accepts the result;
+6. beacon receives or observes result acceptance.
-- the exact code revision;
-- the isolated environment and credential roles;
-- the test command and result;
-- the transport actually observed;
-- cleanup of test artifacts and credentials.
+Successful registration by itself is not sufficient.
-## 7. Treat the E2E workflow as a prerequisite gate
+## 6. Add the dashboard or direct listeners
-The protected `octoc2-live-e2e` workflow is manually dispatched. It builds a
-fresh, pre-enrolled beacon artifact and validates the declared repository,
-credential, recovery, proxy, and optional mTLS configuration. It does not start
-the controller or beacon, execute a task, or establish live E2E success.
+The dashboard requires the controller's opt-in HTTPS API. Configure trusted TLS,
+the operator API token, and the exact beacon bearer map before enabling it. Use
+the complete [Configuration](CONFIGURATION.md#https-and-operator-api) and
+[Dashboard](../dashboard/README.md) guides, then:
-The required topology is:
+```dotenv
+OCTOC2_HTTP_ENABLED=true
+OCTOC2_HTTP_HOST=127.0.0.1
+OCTOC2_HTTP_SERVER_CERT=
+OCTOC2_HTTP_SERVER_KEY=
+OCTOC2_HTTP_CA_CERT=
+OCTOC2_OPERATOR_API_TOKEN=
+OCTOC2_BEACON_API_TOKENS={"":""}
+```
+
+```bash
+cd dashboard
+bun run dev
+```
-- one isolated private C2 repository, which is also the proxy control
- repository for this scenario;
-- one separate private proxy decoy repository;
-- one separate public recovery repository;
-- one full pre-enrolled beacon identity whose public artifact is directly
- importable from `OCTOC2_ENROLLMENT_DIR`;
-- distinct controller GitHub, operator GitHub, operator API, per-beacon API,
- recovery-writer, and proxy-dispatch credentials;
-- server-only GitHub App and recovery-signing private keys.
+Open `http://127.0.0.1:5173`. Keep both Vite and the controller on loopback for
+local operation.
-No GitHub App private key, shared GitHub credential, or static proxy credential
-may be placed in the beacon environment or binary.
+For gRPC/mTLS, Codespaces, OIDC, proxy, Gist, and other GitHub artifact paths,
+follow the exact prerequisites in [Channels](CHANNELS.md). Qualify one channel
+at a time with `ping` before adding it to a failover list.
-To inspect the local fail-closed behavior without querying GitHub:
+## 7. Stop and clean the test
```bash
-bun run scripts/test-end-to-end.ts --dry-run
+bun run octoctl/src/index.ts stop
```
-This command still requires the complete declarations and local files. Missing
-prerequisites are reported explicitly and produce a nonzero exit code. Add
-`--check-github` only in the approved protected environment. Add `--grpc` or
-`--http` whenever those transports appear in the recovery priority so their
-security prerequisites are validated.
-
-Any later live execution is a separate, explicitly authorized manual exercise.
-Record the exact revision, observed transport, task/result evidence, artifact
-cleanup, and credential revocation before using the term "Live E2E verified."
-See [Deployment and assurance limits](PRODUCTION.md) for the full environment
-contract.
+Stop the beacon separately. Inventory and remove only artifacts created by the
+test: issue comments/issues, transport branches/files, variables, deployments,
+Gists, notes refs, proxy workflow artifacts, and temporary binaries. Preserve
+controller/beacon state if the enrollment will be used again.
+
+Do not delete or revoke PATs unless the approved cleanup plan explicitly calls
+for credential revocation. Artifact cleanup and credential lifecycle are
+separate decisions.
+
+## Next steps
+
+- [Architecture](ARCHITECTURE.md) explains identity and result acceptance.
+- [Configuration](CONFIGURATION.md) lists listener, lifecycle, beacon, OIDC,
+ dashboard, and recovery variables.
+- [Channels](CHANNELS.md) covers every transport and permission.
+- [CLI](CLI.md) documents all operator commands.
+- [Troubleshooting](TROUBLESHOOTING.md) maps common log messages and API errors
+ to safe checks.
+- [Operations and assurance](PRODUCTION.md) is the deployment checklist.
diff --git a/docs/README.md b/docs/README.md
index 42331d9..d386817 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,23 +1,75 @@
# OctoC2 Documentation
+OctoC2 is a GitHub-native command-and-control framework for authorized
+security research, with encrypted multi-channel transport and resilient
+failover. This manual covers installation through operation, recovery,
+verification, and development.
+
> [!IMPORTANT]
> **Authorized use only.** Use OctoC2 only on systems and repositories you own
-> or have explicit permission to test. Keep credentials scoped and controller
-> surfaces private.
+> or have explicit permission to test. Keep credentials least-privileged,
+> listeners private, and live-test artifacts inventoried and cleaned.
+
+## Start here
-This directory contains the operator and engineering guides for OctoC2. Start
-with the guide that matches the job in front of you.
+| If you are… | Read these in order |
+|---|---|
+| Evaluating locally | [Installation](INSTALLATION.md) → [GitHub setup](GITHUB_SETUP.md) → [Quickstart](QUICKSTART.md) |
+| Deploying an authorized environment | [Architecture](ARCHITECTURE.md) → [Configuration](CONFIGURATION.md) → [Channels](CHANNELS.md) → [Operations](PRODUCTION.md) |
+| Operating day to day | [CLI](CLI.md) → [Dashboard](../dashboard/README.md) → [Troubleshooting](TROUBLESHOOTING.md) |
+| Configuring resilience | [Proxy contract](../templates/proxy/README.md) → [Recovery](RECOVERY.md) |
+| Reviewing or contributing | [Development](DEVELOPMENT.md) → [Verification traceability](REMEDIATION_TRACEABILITY.md) |
-## Choose a guide
+The [quickstart](QUICKSTART.md) is the shortest path to a pre-enrolled beacon
+and a verified `ping`. The other guides explain the decisions behind each step.
-| Goal | Guide | What it covers |
-|---|---|---|
-| Launch the local stack | [Quickstart](QUICKSTART.md) | Toolchain, credentials, TLS, controller, dashboard, CLI, and verification |
-| Configure an environment | [Operations and assurance](PRODUCTION.md) | Listener policy, replay safety, state lifecycle, certificates, and operational checks |
-| Provision credential recovery | [Recovery](RECOVERY.md) | GitHub App policies, signed recovery records, token leases, renewal, and key rotation |
-| Review implementation evidence | [Remediation traceability](REMEDIATION_TRACEABILITY.md) | Finding-to-code mapping, automated coverage, live qualifications, and cleanup evidence |
-| Operate the web interface | [Dashboard guide](../dashboard/README.md) | Local development, TLS trust, login roles, and activity semantics |
-| Configure a proxy route | [Proxy workflow contract](../templates/proxy/README.md) | Control/decoy topology, workflows, variables, secrets, signatures, and deduplication |
+## Complete guide map
+
+### Learn and install
+
+| Guide | Covers |
+|---|---|
+| [Architecture](ARCHITECTURE.md) | Components, identities, task lifecycle, durable state, transport abstraction, recovery, and trust boundaries |
+| [Installation](INSTALLATION.md) | Pinned toolchain, source install, component builds, platform beacon binaries, updates, and local verification |
+| [GitHub setup](GITHUB_SETUP.md) | Control/decoy/recovery topology, GitHub App UI fields, App permissions, PAT roles, installation, and rotation |
+| [Quickstart](QUICKSTART.md) | Guided and manual first run, enrollment import, controller/dashboard/beacon launch, first task, and cleanup |
+
+### Configure and operate
+
+| Guide | Covers |
+|---|---|
+| [Configuration](CONFIGURATION.md) | Controller, listener, lifecycle, OIDC, recovery, beacon, dashboard, and CLI variables |
+| [Channel guide](CHANNELS.md) | Full channel catalog, permission/prerequisite matrix, per-channel setup, priority, failover, and qualification |
+| [CLI reference](CLI.md) | Setup, key generation, inventory, tasks, results, builds, recovery, proxy, services, and JSON output |
+| [Dashboard guide](../dashboard/README.md) | Local UI, TLS trust, login roles, live/direct modes, and activity semantics |
+| [Operations and assurance](PRODUCTION.md) | Listener exposure, credentials, identity, replay, result acceptance, state, certificates, evidence, and stop conditions |
+| [Troubleshooting](TROUBLESHOOTING.md) | Startup, GitHub errors, decryption, acknowledgements, proxy, TLS, gRPC, Codespaces, OIDC, state, and CI |
+
+### Resilience and verification
+
+| Guide | Covers |
+|---|---|
+| [Recovery](RECOVERY.md) | Deterministic dead-drops, exact App policies, short-lived leases, publication, renewal, and key rotation |
+| [Proxy workflow contract](../templates/proxy/README.md) | Control/decoy workflows, variables, secrets, signed routes, deduplication, and artifact cleanup |
+| [Remediation traceability](REMEDIATION_TRACEABILITY.md) | Finding-to-code mapping, automated evidence, live qualifications, and cleanup records |
+| [Development](DEVELOPMENT.md) | Workspace conventions, contract changes, tests, builds, documentation standards, and change checklist |
+
+## Feature overview
+
+- One signed and encrypted task protocol across GitHub artifacts, HTTPS/WSS,
+ gRPC/mTLS, OIDC, Codespaces, proxy, and relay paths.
+- Thirteen selectable channel kinds, with prerequisites and evidence status
+ reported separately; one reserved catalog entry remains unavailable.
+- Pre-enrolled X25519 encryption and Ed25519 signing identities.
+- Durable SQLite controller state, delivery leases, replay records, cursors,
+ results, and lifecycle state.
+- Persistent beacon task ledger for at-most-once execution across restarts.
+- Authenticated operator CLI and local dashboard with separated credential roles.
+- Signed, generation-numbered recovery records carrying narrowed GitHub App
+ installation-token leases.
+- Strict task catalog with routine, elevated, and destructive risk classes.
+- Five platform beacon build targets: Linux x64/arm64, Windows x64, and macOS
+ Apple silicon/Intel.
## System map
@@ -25,78 +77,51 @@ with the guide that matches the job in front of you.
dashboard / octoctl
│ operator API token
▼
- server ─────────────── SQLite state
- │ │
- │ signed task envelopes │ identities, leases,
- │ │ results, replay state
- ▼ │
-GitHub APIs / HTTPS / gRPC ◄──────┘
+controller ───────────────── durable SQLite state
+ │ identities · tasks · delivery leases
+ │ signed + sealed results · replay records · cursors
+ ▼
+GitHub APIs · HTTPS · gRPC · signed relays
│
▼
- implant
+pre-enrolled beacon ──────── persistent identity + task ledger
```
-The `shared/` workspace defines the canonical task catalog, channel catalog,
-signed envelopes, key identifiers, and result-signature payloads used by every
-component.
-
-## Common workflows
-
-### First local run
-
-1. Install the pinned Bun dependency graph.
-2. Generate and verify protocol bindings.
-3. Create an operator X25519 keypair.
-4. Prepare a private control repository and role-separated credentials.
-5. Configure trusted TLS before enabling HTTP or gRPC.
-6. Start the controller, dashboard, and CLI.
-7. Verify one harmless ping task and its accepted signed result.
-
-Follow the [quickstart](QUICKSTART.md) for commands and configuration.
-
-### Add a transport
-
-1. Check the channel catalog prerequisites in `shared/src/channels.ts`.
-2. Grant only the declared repository permissions.
-3. Keep App private keys on the controller; issue a narrowed installation-token
- lease when the channel supports it.
-4. Add the channel to the beacon priority list.
-5. Run its implant and controller test suites.
-6. Record live task/result evidence and artifact cleanup for external tests.
-
-### Prepare deterministic recovery
-
-1. Create a dedicated public recovery repository.
-2. Provision the recovery Ed25519 signing identity.
-3. Configure exact per-beacon GitHub App policies.
-4. Configure the complete replacement policy for each beacon.
-5. Publish signed, sealed records and verify proactive renewal before lease
- expiry.
-
-The [recovery guide](RECOVERY.md) defines the exact record and policy format.
+The `shared/` workspace is authoritative for channel IDs, task kinds, envelope
+shapes, signatures, and validation rules.
## Configuration families
| Prefix | Component | Purpose |
|---|---|---|
-| `OCTOC2_HTTP_*` | Controller and operator clients | HTTPS/WSS listener, certificates, URL, and CA trust |
-| `OCTOC2_GRPC_*` | Controller | gRPC listener, mTLS material, and certificate fingerprints |
-| `OCTOC2_BEACON_*` | Controller | Per-beacon API credentials and lifecycle thresholds |
-| `OCTOC2_GITHUB_APP_*` | Controller | App identity, private key, and narrowed token policies |
-| `OCTOC2_RECOVERY_*` | Controller and build tooling | Recovery repository, signing identity, publication, and policies |
-| `SVC_*` | Beacon runtime | Enrolled identity, transport credentials, certificates, timing, and recovery trust |
+| `OCTOC2_HTTP_*` | Controller/operator | HTTPS/WSS listener and CA trust |
+| `OCTOC2_GRPC_*` | Controller | gRPC listener, mTLS, and exact certificate fingerprints |
+| `OCTOC2_BEACON_*` | Controller/build | Beacon credentials, lifecycle, and pre-enrollment identity |
+| `OCTOC2_GITHUB_APP_*` | Controller | App identity, private-key file, and exact token policies |
+| `OCTOC2_RECOVERY_*` | Controller/build/beacon | Recovery repository, signing trust, publication, and policy |
+| `SVC_*` | Beacon | Runtime transport credentials, endpoints, timing, and cleanup |
+| `VITE_*` | Dashboard/docs site | Build-time controller/repository coordinates |
-Configuration is fail-closed: malformed security state, missing required TLS
-material, ambiguous identities, unsafe timing, and inconsistent credential
-maps stop startup instead of selecting a weaker fallback.
+Configuration is fail-closed at security boundaries: incomplete TLS material,
+ambiguous credential roles, mismatched identities, malformed policy, and
+unsafe timing stop startup or make the affected transport ineligible.
-## Verification vocabulary
+## Evidence vocabulary
| Term | Meaning |
|---|---|
-| Unit tested | A local automated test exercises one component or contract |
+| Unit tested | A deterministic local test covers one component or contract |
| Integration tested | Multiple local components complete a recorded interaction |
| Live qualified | An authorized external run observes the stated transport boundary and records cleanup |
-| Live task/result | Registration, encrypted task delivery, implant receipt, signed result publication, and controller acceptance complete through the channel |
+| Live task/result | Registration, encrypted task delivery, beacon receipt, signed result publication, and controller acceptance complete through the named channel |
+
+Use the most specific label supported by evidence for the exact revision.
+Catalog presence, successful registration, and artifact publication alone do
+not establish live task/result qualification.
+
+## Getting help
-Use the most specific term supported by the evidence for the exact revision.
+Check [Troubleshooting](TROUBLESHOOTING.md), then capture the exact revision,
+component versions, redacted logs, channel, and failing command. Never attach
+dotenv files, PATs, private keys, bearer values, private-repository content,
+hostnames, or usernames to a public report.
diff --git a/docs/TROUBLESHOOTING.md b/docs/TROUBLESHOOTING.md
new file mode 100644
index 0000000..fc2668e
--- /dev/null
+++ b/docs/TROUBLESHOOTING.md
@@ -0,0 +1,186 @@
+# Troubleshooting
+
+> [!IMPORTANT]
+> **Authorized use only.** Diagnose only systems and repositories included in
+> the approved test boundary, and redact sensitive data from every artifact.
+
+Start with a harmless `ping`, one channel, and `OCTOC2_LOG_LEVEL=debug`. Record
+timestamps from controller and beacon logs, but redact credentials, private
+keys, authorization headers, repository URLs that identify a private customer,
+hostnames, and usernames before sharing diagnostics.
+
+## First checks
+
+```bash
+octoctl status
+octoctl beacons --json
+octoctl tentacles list --beacon --verbose
+octoctl results --last 3 --json
+```
+
+Confirm system clocks are synchronized, the exact beacon ID matches the
+enrollment, and the configured channel appears in the beacon's startup log.
+
+## Startup failures
+
+| Symptom | Likely cause | Check or fix |
+|---|---|---|
+| Missing required configuration | Repository, operator key, credential, or recovery bootstrap is incomplete | Run `octoctl setup --phase validate`; compare with [Configuration](CONFIGURATION.md) |
+| Operator secret has invalid length | Wrong encoding or wrong key role | Regenerate with `octoctl keygen`; use the base64url X25519 secret |
+| No pre-provisioned identity | Beacon was compiled in simple mode or state was removed | Build/enroll with full `octoctl build-beacon`; do not invent key fields |
+| Provisioned identity does not match state | A binary/state directory from different enrollments was combined | Restore the matching pair; never overwrite identity checks |
+| Both static token and lease configured | Ambiguous GitHub authority | Set exactly one of `SVC_GITHUB_TOKEN` and `SVC_GITHUB_TOKEN_LEASE` |
+| App private key rejected on beacon | Server-only secret was placed on endpoint | Remove it from the endpoint; mint narrowed leases on the controller |
+| Listener fails immediately | Invalid port, missing TLS files, unreadable key, or occupied port | Validate paths/permissions and inspect the named bind address |
+
+## GitHub API errors
+
+### `404 Not Found`
+
+For private repositories GitHub often returns `404` when the credential cannot
+see the repository. Check the credential's resource owner, selected repository,
+App installation, and expiration before assuming the artifact is missing. Also
+confirm the issue/comment/branch/ref still exists and that owner/repo case is
+correct.
+
+### `401 Unauthorized`
+
+The credential is absent, malformed, revoked, or expired. A short-lived App
+lease may have expired; use the proactive recovery publisher or provision a new
+lease. Do not substitute a different credential role merely to make the call
+succeed.
+
+### `403 Forbidden`
+
+The identity is recognized but lacks the required permission, is blocked by an
+organization policy, or hit a rate/abuse limit. Compare the channel with the
+[permission matrix](GITHUB_SETUP.md#repository-permissions), inspect GitHub's
+response headers, and wait only when the failure is actually rate-related.
+
+## Issues and proxy symptoms
+
+### Registration succeeds, but comments return 404
+
+Registration only proves one API path worked. Verify the token can read and
+write comments on the exact configured issue, and that the controller watches
+the same owner/repo/issue scope. For proxy, inspect both the decoy issue and the
+control-side relay artifact.
+
+### `failed to decrypt task comment`
+
+The comment may be a registration acknowledgement/result rather than a task,
+may belong to another beacon, may be malformed, or may have been sealed with a
+different operator/beacon key. Confirm `MONITORING_PUBKEY`, enrollment keys,
+beacon ID, route issue number, and recovery generation. Do not weaken parsing
+to accept an ambiguous payload.
+
+### `payload is neither a task array nor registration ack`
+
+An unrelated/stale comment was read or the two route endpoints disagree about
+the envelope version. Confirm artifact scoping and deploy the same revision to
+beacon, controller, and proxy workflows. Remove stale test artifacts only after
+capturing the evidence needed to diagnose them.
+
+### Result acknowledgement timeout
+
+An artifact write is not the same as controller acceptance. Look for the result
+in controller logs and inspect signature, ownership, task state, replay, and
+result-digest rejection messages. Confirm the acknowledgement is returned on
+the same issue/route. Increase `SVC_RESULT_ACK_TIMEOUT_MS` only when acceptance
+eventually succeeds and measured queue latency exceeds the current window.
+
+### Proxy fails while Issues works
+
+Check that control and decoy are distinct, both workflows are present and
+enabled, repository-dispatch secrets target the correct opposite repository,
+the App installation includes the decoy, route signatures/fingerprints match,
+and the signed recovery policy contains the current route. Inspect Actions logs
+on both repos without printing secret values.
+
+## Direct transport symptoms
+
+### Certificate verification or hostname failure
+
+Use the hostname present in the server certificate SAN. Verify the configured
+CA file, certificate chain, validity dates, and target clock. There is no
+supported insecure TLS switch.
+
+### HTTP returns 401/403
+
+The dashboard/CLI uses `OCTOC2_OPERATOR_API_TOKEN`; a beacon uses the token
+mapped to its exact ID in `OCTOC2_BEACON_API_TOKENS`. Do not interchange them.
+Credentials belong in authorization headers, never query strings.
+
+### gRPC cannot connect
+
+Confirm the listener is enabled, port is reachable, the client trusts the
+server CA, the server trusts the client CA, the beacon certificate fingerprint
+matches the exact ID, and the per-beacon bearer is present. A Dev Tunnels HTTPS
+URL is not a raw gRPC endpoint.
+
+### Codespaces discovery or SSH fails
+
+Run `gh auth status` under the same OS user, verify the dedicated classic PAT
+has `codespace`, confirm the named Codespace belongs to/permits that account,
+and test approved SSH access. Deep-sleep/stopped Codespaces may need to start
+before discovery completes. Auto-provisioning occurs only when explicitly
+enabled.
+
+## OIDC rejection
+
+Compare the token claims to `OCTOC2_OIDC_BINDINGS`: repository, subject,
+workflow ref, audience, and exact beacon ID. Confirm the workflow has
+`permissions: id-token: write`. Ref or reusable-workflow changes can alter
+claims. Do not add wildcards to bypass a mismatch.
+
+## State and recovery
+
+| Symptom | Check |
+|---|---|
+| Beacon appears under a new ID | Wrong/empty state directory or non-enrolled build |
+| Task repeats after restart | Preserve the beacon ledger and controller database; inspect replay-retention changes |
+| Recovery record ignored | Signature/key ID, recipient ID, generation monotonicity, expiry, repo/ref, and complete policy |
+| Recovered token works on wrong repo | Treat as a policy failure; stop testing and inspect App installation/policy narrowing |
+| Controller loses history | Restore the whole configured data directory, not selected JSON/SQLite files |
+
+## Dashboard
+
+- Blank API data: verify `VITE_C2_SERVER_URL` is a bare HTTPS origin and the
+ controller HTTP listener is enabled.
+- Browser certificate warning: trust the issuing internal CA in the browser/OS;
+ do not disable verification in Vite.
+- Login succeeds in GitHub mode but live actions fail: GitHub PAT and operator
+ API token are separate roles.
+- CORS rejection: set the reviewed `OCTOC2_DASHBOARD_ORIGIN` to the exact UI
+ origin.
+
+## Development and CI
+
+Run the narrowest failing workspace first:
+
+```bash
+bun test --timeout 30000
+bun run typecheck
+```
+
+Then run repository policy checks from the root. If dashboard tests leak mocks
+or DOM state, use its isolated `src/testRunner.ts` rather than replacing it with
+a generic root test invocation. If `proto:check` fails, regenerate bindings and
+review the diff instead of editing generated files manually.
+
+## Safe live-test sequence
+
+1. Record the exact commit and channel.
+2. Confirm private repository and credential scope.
+3. Start controller, then beacon, and wait for an authenticated registration.
+4. Submit one `ping` forced through the named channel.
+5. Record publication, receipt, signed result, and controller acceptance.
+6. Remove test issues/comments, refs, variables, deployments, Gists, images,
+ notes, and workflow artifacts created by the run.
+7. Confirm normal non-test content remains.
+8. Retain or revoke PATs only according to the explicit credential cleanup
+ decision; artifact cleanup does not authorize token deletion.
+
+Stop immediately if a route reaches an unapproved repository, a credential has
+wider access than intended, plaintext secret/PII appears in an artifact or log,
+or identity/ownership verification is bypassed.
diff --git a/scripts/check-doc-consistency.ts b/scripts/check-doc-consistency.ts
index b537f68..6fd04e0 100644
--- a/scripts/check-doc-consistency.ts
+++ b/scripts/check-doc-consistency.ts
@@ -5,6 +5,14 @@ const root = join(import.meta.dir, "..");
const publicDocs = [
"README.md",
"docs/QUICKSTART.md",
+ "docs/INSTALLATION.md",
+ "docs/GITHUB_SETUP.md",
+ "docs/ARCHITECTURE.md",
+ "docs/CHANNELS.md",
+ "docs/CLI.md",
+ "docs/CONFIGURATION.md",
+ "docs/TROUBLESHOOTING.md",
+ "docs/DEVELOPMENT.md",
"docs/PRODUCTION.md",
"docs/RECOVERY.md",
"docs/REMEDIATION_TRACEABILITY.md",