AGENTS.md (26804B)
1 # myc — repository agent contract 2 3 ## 1. Scope and operating model 4 5 - This file applies to the complete repository unless a nearer `AGENTS.md` is 6 stricter. 7 - This repository owns `myc`, the standalone Radroots NIP-46 signer service. 8 Treat identity custody, signing, approval, session, persistence, and 9 signer-facing transport as security-critical behavior. 10 - Keep the repository independently buildable, testable, packageable, and 11 operable. Do not depend on private repositories, unreachable or unlocked 12 artifacts, internal monorepo paths, absolute workstation paths, or private 13 harnesses. An unpublished public dependency is allowed only when its exact 14 commit is reachable from the governed public Git source and pinned by the 15 checked-in source lock. 16 - `.github/**` and capsule-local CI workflows are forbidden; keep validation 17 forge-agnostic, and place any required monorepo orchestration exclusively 18 under the parent monorepo's root `.act/**` authority. 19 - Do not add or retain tracked `docs/**`, `.github/**`, or `.act/**` content in 20 this capsule. Human-facing Myc specifications, decisions, runbooks, and 21 qualification evidence belong under the parent monorepo's 22 `docs/oss/myc/**` authority; standalone machine-enforced declarations belong 23 under this repository's governed contract surfaces. 24 - Myc does not own relay storage or tenancy, general relay fanout, SDK contract 25 generation, wallet UX, hosted accounts, telemetry, artifact promotion, or 26 deployment transport. 27 28 ## 2. Authority and preflight 29 30 - Before editing, read this file, `README`, `Cargo.toml`, 31 `radroots.service.source-lock.v2.toml`, the relevant implementation and tests, 32 and `flake.nix` or migrations when they are in scope. 33 - `.radroots-consumer-root` is the standalone source-lock identity and must 34 remain exactly `myc`. The implemented control-plane contract is 35 `contracts/services_hardening/operator_contract.v1.json`. 36 Service implementation must use its exact routes, operation IDs, model 37 fields, doctor checks, and shared host/exit references; prototype CLI or 38 HTTP behavior is not authority to reinterpret that contract. 39 - The exact signer-provider inventory and resource boundary is 40 `contracts/services_hardening/provider_contract.v1.json`. Keep its role, 41 provider, capability, call-identity, deadline, limit, credential-reference, 42 cancellation, and no-publication facts synchronized with the sealed Rust 43 models. Provider wire encoding and result verification may refine only the 44 later checkpoints that own those boundaries; they may not widen this 45 contract. 46 - The encrypted-file implementation is frozen by 47 `contracts/services_hardening/encrypted_identity_envelope.v1.json`. It must 48 use the source-locked `radroots_secrets` v2 context-bound envelope, explicit 49 caller-supplied entropy, create-new owner-only persistence, expected-public- 50 key verification, and state-backup exclusion. Credential artifact resolution 51 remains a separate boundary and may not introduce a sibling-key fallback. 52 - Wrapping-credential resolution is frozen by 53 `contracts/services_hardening/wrapping_credential_resolution.v1.json`. It 54 derives one validated shared artifact name beneath the canonical instance 55 secrets root, reads only an existing exact owner-only artifact, and exposes 56 neither a caller path nor caller bytes. Production injection/mounting and 57 repo-local provisioning are external/offline; ordinary run, TOML, 58 environment, arguments, envelope siblings, and state backup never create or 59 carry the credential. 60 - Local-signer transport is frozen by 61 `contracts/services_hardening/local_signer_transport.v1.json`. It uses the 62 hardened Lib `AdminClient` for one fixed HTTP/1.1 JSON endpoint over a Unix 63 socket, carries the complete provider-operation binding in closed tagged 64 request/response models, enforces configured body/deadline/concurrency 65 limits, and returns only semantically untrusted output for Step 135. 66 - Provider verification is frozen by 67 `contracts/services_hardening/provider_verification.v1.json`. It requires 68 injected observation time, rebinds the complete response to the configured 69 provider and original operation, verifies peer/direction/version and exact 70 protocol shapes, cryptographically verifies signed events, retains their 71 exact canonical bytes, and returns only a sealed redacted result. 72 Verification is not publication and performs no provider execution or 73 database mutation. 74 - Step 136 removes the orphaned prototype provider tree and its keyring, 75 managed-account, plaintext/adjacent-key, child-process, implicit-identity, 76 generic remote-session, legacy logging/client, and unused dependency 77 surfaces. Do not restore those files, dependencies, or Tokio process 78 capability; `radroots_nostr_connect` remains only as the active NIP-46 79 protocol dependency. 80 - Step 137 owns only the existing-state runtime foundation and exact startup 81 prerequisite inventory. Encrypted-file opening runs in joined one-shot 82 supervisor tasks; local-signer construction performs no probe, and no 83 provider is ready before its governed verification. Do not add detached 84 handles, a library runtime, signals, process exit, relay/admin execution, or 85 a false readiness transition here. 86 - Step 138 freezes a root-only public API. Keep every implementation module 87 private, expose only curated crate-root names, keep public errors crate-owned 88 and source-free, and update the reviewed API baseline and package guards for 89 every intentional public-surface change. Shared runtime-path, SQLite, and 90 storage identity types are deliberate governed contract dependencies; 91 provider, SQLx, Serde, transport, and task implementation types are not. 92 - Step 139 established the predecessor service source lock and pre-promotion 93 native package metadata. Keep the exact Lib revision consistent across every 94 direct Radroots dependency, Cargo.lock, the verified source archive, and the 95 generated v2 service lock. Deferred `flake.nix` and `flake.lock` material is 96 independently digest-bound and may select an older reachable Lib revision; 97 it is not active native revision authority. Native target metadata does not 98 qualify an artifact; Nix, OCI, signing, tags, publication, and deployment 99 remain deferred. 100 - RCLD-RSHR-195 Step 245 advances the active native Lib source lock and freezes 101 Myc state creation behind the runtime-path directory plan plus the sealed 102 service-SQLite initializer. Explicit initialization may provision only the 103 exact governed service-instance suffix after identity and catalog validation; 104 every existing-only open remains non-creating. Do not restore raw paths, raw 105 SQLx connections, filesystem probes, or directory-creation fallbacks at the 106 state-host boundary. 107 - Step 148 closes the production NIP-46 response authority in 108 `contracts/services_hardening/nip46_response_commit.v1.json`. Production 109 code may commit a completion only through the atomic exact-response method; 110 a retained completion without its immutable response and initial delivery 111 state is inconsistent evidence and must never be repaired implicitly. 112 - Step 150 introduced the historical 21-route and 35-model Myc Unix-admin 113 adapter. Step 159 unit 13 owns its final 19-route and 32-model form by 114 removing live identity rekey and replacement. Keep the shared Lib router 115 private; reject model drift, unknown/duplicate/null 116 fields, noncanonical response bytes, invalid path/query values, and unsafe 117 errors at the adapter boundary. Domain handlers must bind authenticated 118 cursors to route/filter/snapshot identity and must retain exact operation-ID 119 replay/conflict evidence before returning success. Do not infer relay 120 delivery from a successful local mutation and do not add TCP admin routes. 121 - Step 152 freezes the one-parse CLI execution split. Every admitted command 122 must select exactly daemon, offline, or permissioned Unix-admin authority; 123 only the explicitly contracted read-only status, backup, and public-identity 124 operations may fall back after proving the daemon writer lock is free. 125 Identity rekey and replacement are offline create-new/config-apply 126 operations and have no live command or route. Every retained live mutation 127 has no direct-state fallback. 128 Do not reparse process arguments or let a live CLI plan obtain SQLite, 129 provider, relay, task, signal, or runtime authority. 130 - Step 153 freezes one ordered 13-check doctor engine. Check adapters retain 131 their operation-specific authority and may return only closed observations; 132 the engine owns exact deadlines, required/optional aggregation, fixed safe 133 summaries/remediation codes, bounded canonical JSON, and exit 6 for required 134 failure or timeout. Do not admit raw errors, paths, URLs, keys, credentials, 135 arbitrary details, unbounded output, detached probe work, or 136 liveness/readiness probe authority. A pass must prove every contracted scope 137 facet, and deadline cancellation must stop or synchronously own cleanup. 138 - Step 154 freezes one passive latest-value status cache around the shared Lib 139 lifecycle primitive. The non-clone publisher encodes the complete bounded v1 140 local-status envelope before atomic replacement; cloneable readers may only 141 return the retained immutable snapshot. Status reads never query SQLite, 142 providers, relays, credentials, DNS, time, or fresh probes and never spawn a 143 task. Keep connection-count keys and identity roles closed, preserve the last 144 valid snapshot on any failed publication, admit only the fixed twelve status 145 reasons, and keep detailed status on the permissioned Unix-admin boundary. 146 - Step 159 unit 10 owns schema-v10 offline configuration lifecycle. Keep the 147 configuration-binding ledger append-only and capped at 1,024 generations; 148 seed one post-migration generation without rewriting the immutable birth 149 record. Startup must match the latest config/public-identity binding. Identity 150 changes revoke live connection/challenge authority, permission narrowing 151 revokes affected sessions only, and an existing relay referenced by 152 nonterminal delivery work cannot be removed or changed. Do not persist relay 153 URLs, paths, credentials, provider envelopes, or protected values in the 154 binding history. 155 - Step 159 unit 11 owns schema-v11 bounded admin idempotency. Keep operation 156 identifiers on the fixed ASCII grammar, bind route plus canonical request 157 digest, cap replay models at 8,192 bytes, prune only expired Completed rows, 158 reserve completion capacity at admission, and retain unresolved Prepared 159 evidence as outcome-unknown. The configured admin response cap must admit the 160 maximum model in its bounded success envelope. Never persist a 161 request body, path, correlation ID, credential, bundle path, or secret in the 162 journal. Database-only mutations must later compose their effect, audit, and 163 completion in one transaction; online backup records Prepared before capture 164 and completes only after the bundle is durable. 165 - Step 159 unit 12 owns the crate-private provider executor, the exact 166 source-locked `radroots_transport_nostr` adapter, and the durable delivery 167 worker. Provider results remain untrusted until independently verified. 168 Delivery preparation performs no relay I/O; the worker persists Submitted 169 immediately before execution, maps post-submit cancellation or lost 170 acknowledgement to UnknownAcknowledgement, and retries only the exact 171 committed signed bytes. Never hold a SQLite transaction across provider or 172 relay work, detach protected blocking work, expose the executor/client, or 173 create one task per relay. Unit 15 alone wires these components into the 174 fixed runtime graph and startup handshake. 175 - Step 159 unit 13 owns the final 19-route/32-model production Unix-admin 176 server around the exact handler boundary, the configured transport-limit 177 projection, the canonical permissioned `admin.sock` binding, and the 178 machine-bound composition facts for the existing sole status publisher, 179 passive three-route operations server, and injected 13-check doctor. Keep 180 shared routers, listeners, entropy, and cancellation private. Unit 14 owns 181 secure CLI/config bootstrap and concrete doctor probes; Unit 15 alone owns 182 server task spawning, provider/relay wiring, readiness, reconnect, and 183 shutdown. 184 - Step 159 unit 14 owns the one-pass process executor, descriptor-bound config 185 loading and create-new persistence, fixed zeroizing identity-provisioning 186 input, actual existing-state metadata discovery, explicit backup/restore 187 inputs, one binary-owned configured Tokio runtime, and concrete bounded 188 doctor probes. Keep result bytes on stdout and fixed diagnostics on stderr. 189 The executable must not provision deployment directory trees, derive runtime 190 limits from host CPUs, read secret arguments or environment variables, open 191 an existing live database before validating a restore manifest, publish from 192 doctor, or return success without the Unit 15 daemon `run` graph. Unit 15 193 owns that graph, process signals, readiness/reconnect, and phase-aware drain. 194 - Step 160 owns the standalone native release-artifact boundary in 195 `contracts/services_hardening/native_release.v2.json` and `cargo xtask 196 native-release`. Keep its exact two Linux targets, clean committed source, 197 caller-supplied binary, positive deterministic epoch, bounded generated 198 inventory, vendored offline source archive, source lock, CycloneDX SBOM, 199 notices, checksums, unsigned provenance, and fixed systemd template closed. 200 Outputs remain external to the source tree. Do not accept a caller service 201 root, arbitrary member name, Nix/NixOS/OCI input or output, signing key, 202 parent-owned human document, private harness, protected material, or 203 publication/deployment authority. 204 - RCLD-RSHR-150 Step 227 owns the fixed standalone systemd unit and 205 `systemd_qualification.v1.json`. Keep the canonical service-host directory 206 directives, stable exit-code restart split, bounded stop, empty capability 207 sets, no environment-carried credentials, systemd 252 minimum, and maximum 208 offline exposure 3.0 exact. The Linux verifier must fail closed when 209 `systemd-analyze` is absent. Do not enable compatibility-sensitive 210 `MemoryDenyWriteExecute` or syscall filters until the Step 229 integration 211 wave proves them against the real binary; do not install, enable, start, or 212 deploy a production service here. 213 - Step 221 integration requires a signer-transport-authored `pending_connection` response for an 214 explicitly approval-gated NIP-46 connect request. Keep that exact response 215 and its initial delivery job atomic and immutable without recording a false 216 terminal operation completion; relay delivery and exact replay use only the 217 retained signed bytes, and terminal response authority must not conflict 218 with the pending response. 219 - Step 161 owns the closed executable qualification matrix in 220 `contracts/services_hardening/process_qualification.v1.json`. Keep its 221 process deadlines, parallelism, soak count, crash fixture, output bound, 222 actual-process corpus, and component evidence exact. Crash injection is an 223 external test-process termination only; do not add a production failpoint, 224 hidden command, environment selector, detached test worker, or public test 225 API. Failed backup and restore work must retain collision/recovery evidence, 226 preserve the live database, and recover only through the governed next 227 process open. 228 - Treat checked-in source, tests, and prototype behavior as implementation 229 evidence, not permission to preserve behavior that the active requirement 230 removes. 231 - Do not invent protocol behavior, APIs, dependencies, release processes, 232 identity authority, migration behavior, or external integration semantics. 233 - Inspect `git status --short`, the exact repository root, and nearby tests 234 before changing behavior. Preserve unrelated work and stop on an unresolved 235 security or custody conflict. 236 - Keep changes narrowly scoped and independently reviewable. Do not mix 237 unrelated cleanup, speculative abstractions, roadmap work, or compatibility 238 scaffolding into a checkpoint. 239 240 ## 3. Clean-slate service rule 241 242 - Do not add or preserve prototype configuration readers, `.env` or 243 `--env-file` runtime configuration, `MYC_*` runtime selectors, JSON/JSONL 244 mutable state, prototype config/state importers or migrations, worker paths, 245 old-path probes, aliases, fallbacks, dual readers/writers, deprecated 246 modules/APIs/re-exports, or old/new feature switches. Offline production 247 schema migration must never accept an unreleased prototype format. 248 - Remove superseded behavior and update every affected Radroots-owned consumer 249 directly. Do not hide a breaking change behind a compatibility adapter unless 250 an accepted public requirement explicitly requires one. 251 - Preserve canonical NIP-04, NIP-44, and NIP-46 interoperability. Clean-slate 252 product behavior never authorizes protocol drift or relaxed wire validation. 253 - A breaking config, CLI, state, provider, admin, error, or wire change must 254 update its public machine contracts, examples, tests, generated surfaces, and 255 release qualification in the same coherent sequence. 256 257 ## 4. Identity, provider, and secret boundaries 258 259 - Keep transport, user, and optional discovery identities explicit. Never 260 generate, replace, infer, or collapse an identity during ordinary `run`. 261 - Support only the governed `encrypted_file` and permissioned Unix-socket 262 `local_signer` providers. Do not add plaintext keys, arbitrary child 263 commands, shells, desktop/server keyrings, managed accounts, TCP signers, or 264 sibling wrapping-key fallback. 265 - Treat every provider result as untrusted. Verify contract version, operation 266 and correlation IDs, expected identity and role, bounds, exact unsigned event 267 fields, author, event ID, signature, and applicable NIP semantics before use. 268 - Keep plaintext keys, decrypted key material, wrapping credentials, provider 269 secrets, and equivalent protected material out of config, logs, status, 270 metrics, audit output, fixtures, backups, process arguments, environment 271 contracts, error strings, and ordinary `Debug` output. A governed backup may 272 contain the encrypted ciphertext envelope, but never the material needed to 273 unwrap it. Minimize and zeroize protected values where practical. 274 - Use typed request, response, approval, permission, session, provider, and 275 error models. Raw provider, relay, SQL, or source-chain errors never cross a 276 public or operator boundary. 277 278 ## 5. Configuration, state, and process boundaries 279 280 - Load exactly one immutable, strictly versioned TOML document. Reject unknown 281 fields, implicit relays, unsafe defaults, environment overlays, includes, 282 interpolation, fragments, hot reload, and arbitrary leaf flags. 283 - Each service instance owns one explicitly initialized SQLite catalog and one 284 live writer lock. Normal `run` opens existing state only and never creates, 285 imports, guesses, or silently migrates prototype state. 286 - Keep raw SQLite pools and write authority private to the store. The daemon is 287 the only live writer; live mutations and online backup use the typed, 288 permissioned Unix-socket admin boundary and never fall back to direct writes. 289 Offline state operations must prove that no daemon writer lock is held. 290 - Never hold a database transaction while waiting for a provider, relay, DNS, 291 clock, entropy, or other external effect. 292 - Parse the process CLI and initialize the tracing subscriber only in the 293 binary composition boundary. Library modules may emit tracing events but 294 must not install signal handlers, create nested Tokio runtimes, call 295 `process::exit`, or detach authoritative tasks. 296 - Inject wall time, monotonic time, entropy, providers, transport, and 297 failpoints. Supervise and join every authoritative task; panic, error, or 298 unexpected successful return from a critical task must coordinate shutdown 299 and produce a nonzero process result. 300 - Keep the Myc critical-task graph bounded and sealed. A task receives only its 301 cooperative cancellation observer; callers cannot name tasks, extract task 302 handles, detach work, install signals, or select process exits through this 303 library boundary. Step 159 owns signal and forced-shutdown composition. 304 305 ## 6. Admission, commit, and publication invariants 306 307 - Bound and validate signed event bytes, tags, authored time, recipient, 308 signature, event ID, decrypted plaintext, request identity, method, replay, 309 conflicting reuse, connection admission, authorization challenges, and rate 310 retention before accepting work. 311 - Keep authorization-challenge URLs and display-only client metadata under 312 operator policy; untrusted clients never choose redirect or display 313 authority. Use separate bounded rate budgets for connection admission and 314 authorization challenges so exhaustion of one cannot bypass or disable the 315 other. 316 - Commit the request decision, session effects, audit, exact serialized signed 317 response bytes, immutable target set, and initial outbox state atomically 318 before any relay submission. 319 - Treat stored signed bytes as the sole publication authority. Retry, crash 320 recovery, and reopen must submit the identical bytes and digest without 321 deserializing, rebuilding, re-signing, or changing targets. 322 - Create delivery jobs only inside the atomic signed-response or discovery 323 transaction. Recovery is a bounded, cursor-driven repository operation with 324 injected time/jitter evidence; final supervised startup looping remains a 325 later runtime owner. 326 - Render NIP-05 only from verified committed discovery state through an 327 explicit desired/current offline operation. The service library must not 328 silently host the document or acquire network authority while rendering it. 329 - Distinguish submitted, delivered, failed, and unknown outcomes. Lost 330 acknowledgement never becomes proof of failure or delivery. 331 - Bound every queue, pool, request, response, event, tag set, retry schedule, 332 deadline, rate window, retention set, audit query, and in-memory collection. 333 Saturation must reject or defer safely without dropping committed work. 334 335 ## 7. Admin and observability boundaries 336 337 - Detailed status and every live mutation use bounded, versioned HTTP/JSON over 338 the permissioned Unix socket. Do not add TCP admin, browser auth, CORS, or a 339 direct writable CLI fallback. 340 - Optional TCP operations expose only cached `/livez`, `/readyz`, and 341 `/metrics`. They must not perform SQLite, provider, relay, credential, DNS, 342 or other active probes. 343 - Keep the Myc TCP adapter sealed around the source-locked service-host server. 344 Do not add route registration, raw listener/server access, dependency-owned 345 public types, or a second independently observed lifecycle cache. Publish 346 only the fixed cached phase/readiness metric families and closed phase label. 347 - Keep logs as safe structured stderr output. Keep result data on stdout and 348 diagnostics on stderr. Use stable bounded public codes and messages, bounded 349 metric labels, and explicit redaction. 350 - Emit only the sealed `MycLogRecord` vocabulary. Do not log caller text, raw 351 errors or sources, paths, SQL, relay URLs, identifiers, credentials, 352 protected content, or decrypted payloads. Keep the exact 0-6 process result 353 mapping synchronized with the operator contract; do not call process exit 354 from library code or add file logging. 355 - Backup and restore must preserve lock, manifest, integrity, schema, service, 356 instance, identity, permission, fsync, atomic-rename, and protected-material 357 exclusion invariants. 358 359 ## 8. Rust and test discipline 360 361 - Prefer pure transformations, explicit state machines, validated newtypes, 362 tagged enums, narrow side-effect boundaries, and private visibility. 363 - Avoid hidden production panics. Use typed errors for expected failures and 364 reserve `unwrap` or `expect` for tests or locally proven invariants. 365 - Keep `#![forbid(unsafe_code)]` at the crate roots; unsafe code is forbidden. 366 - Add deterministic positive, negative, boundary, crash/retry, cancellation, 367 saturation, redaction, and interoperability tests for every behavior change. 368 Tests and examples must not contain real secrets, realistic private keys, 369 reusable credentials, or sensitive event content. 370 - Treat generated files as generated. Update them through the owning command 371 and run the corresponding freshness check. 372 373 ## 9. Canonical verification 374 375 Through RCLD-RSHR-170, run the standalone native command authority through 376 extbuild. Do not install, repair, invoke, or require Nix, and do not claim Nix, 377 NixOS-module, or Nix-produced OCI qualification: 378 379 ```text 380 cargo extbuild doctor 381 cargo extbuild run -- cargo fmt --all --check 382 cargo extbuild run -- cargo check --workspace --locked 383 cargo extbuild run -- cargo test --workspace --all-targets --locked 384 cargo extbuild run -- cargo clippy --workspace --all-targets --locked -- -D warnings 385 cargo extbuild run -- ./scripts/verify-boundaries.sh 386 cargo extbuild run -- ./scripts/verify-supply-chain.sh 387 cargo extbuild run -- ./scripts/verify-systemd.sh 388 cargo extbuild run -- ./scripts/release-acceptance.sh 389 ``` 390 391 The supply-chain gate requires exact cargo-deny 0.19.8 and cargo-vet 0.10.2, 392 the checked-in exemption inventory, the locked graph, approved licenses and 393 sources, and only the explicitly justified Nostr 0.44 advisories. Exemptions 394 are visible accepted review debt, not claims of independent source audits. 395 The release-acceptance contract requires formatting, locked metadata, locked 396 all-target checking and testing, warnings-denied all-target Clippy, rustdoc with 397 warnings denied, and diff hygiene. Run any gate not yet covered by the current 398 release script explicitly; do not describe the script as sufficient until it 399 enforces the complete contract. Run additional SQLx freshness, source-lock, 400 systemd, package, SBOM, checksum, notice, and fresh-install gates when their 401 surfaces change. Checked-in Nix material remains deferred source data through 402 RCLD-RSHR-170 and is not a verification gate. Every OCI production or 403 qualification path is likewise deferred and unclaimed through RCLD-RSHR-170. 404 Use narrower checked-in commands only for iteration, and never claim a command 405 passed unless it ran successfully. 406 407 ## 10. Commits and irreversible actions 408 409 - Format commits as `<scope>: <imperative summary>`, with a blank line and 410 `- ` bullets when a body is useful. Split unrelated changes. 411 - Report the exact files changed, behavior changed, commands run, results, 412 unresolved risks, and whether the next checkpoint is safe. 413 - Do not publish, push, tag, sign, deploy, rotate credentials, change ownership, 414 or mutate trusted-publisher or external runtime state without explicit 415 authorization for that exact action.