- ADR-001: inherit the alknet TLS design as the baseline; deviations recorded as alktls ADRs - ADR-002: TlsError ships the ADR-088 six-variant shape from day one (typed #[from] sources; NoqWrap; no string catch-all) - ADR-003: the QUIC feature is noq (iroh's extracted fork), pre- consumer rename; default = [] per the lean-crate convention (corrects the extracted code's default = ["quinn"]) - ADR-004: complete accessors — for_tcp_tls() adopted, rustls_config() adopted; server accessors borrow (&self), client accessors consume - ADR-005: identity + credentials + fingerprint types move into alktls; auth layer stays out - ADR-006: eight-module layout; seed tests + integration invariant pins (exact nine-scheme list, client enable_early_data) - specs: overview (transport picture, terminology), server.md (ACME lifecycle, invariants), client.md (verifier selection matrix, root- store fallback); open-questions.md promotes OQ-TLS-01..08 (all resolved at entry) - Cargo.toml: quinn feature -> noq (per ADR-003); AGENTS.md aligned Architecture review pass done: 0 critical, 2 major (ADR-002 AcmeConfig doc comment contradiction; ADR-003 unrecorded default deviation) and 8 minors all addressed; cross-references verified against alknet ADRs, rustls/noq/iroh sources. Verified: cargo test, test --all-features, clippy -D warnings, fmt --check, doc --no-deps
210 lines
11 KiB
Markdown
210 lines
11 KiB
Markdown
# AGENTS.md
|
|
|
|
Operating instructions for opencode agents working in this repo. opencode
|
|
auto-loads this file as instructions, overriding the built-in defaults for
|
|
this project. Custom agents in `.opencode/agents/` inherit these rules
|
|
unless their own prompts say otherwise.
|
|
|
|
## Git Workflow
|
|
|
|
**Commit and push when reasonable.** When a change is complete and
|
|
verified (build + lint + tests pass), commit and push to `origin/main`
|
|
without asking. This overrides the built-in default of "only commit when
|
|
explicitly asked."
|
|
|
|
The workflow:
|
|
|
|
1. Make the change
|
|
2. Verify: `cargo test`, `cargo clippy --all-targets -- -D warnings`,
|
|
`cargo fmt --check`, `cargo doc --no-deps` if docs changed
|
|
3. Inspect `git status` and `git diff` before staging — stage only the
|
|
intended files, never secrets
|
|
4. Write a concise commit message matching the repo style (see `git log
|
|
--oneline -10`). For multi-point changes, use a summary line plus a
|
|
body with bullet points and a verification block.
|
|
5. `git push origin main`
|
|
6. Report the commit hash and the verification summary
|
|
|
|
Exceptions — **do not** commit or push without asking:
|
|
|
|
- The change is exploratory / speculative (you're not sure the user wants
|
|
it kept)
|
|
- The user is actively reviewing the diff and may ask for changes
|
|
- The change touches a public API surface that consumers implement or
|
|
compile against (one-way doors — see "Public API shapes are one-way
|
|
doors" below; once consumers exist, those signatures are stable
|
|
contracts)
|
|
- You'd be force-pushing, amending a published commit, creating an empty
|
|
commit, or skipping hooks
|
|
|
|
Never commit secrets, keys, or credentials. If a commit fails or hooks
|
|
reject it, fix the issue and create a new commit — do not amend the
|
|
failed one.
|
|
|
|
Git identity is preconfigured (`glm-5.3-flash <glm-5.3-flash@alk.dev>`). Do
|
|
not change `git config`, skip hooks, or use `git commit -i`.
|
|
|
|
## Project Conventions (Rust / TLS crate)
|
|
|
|
This is the TLS crate — shared TLS setup types for the alk* stack:
|
|
server and client `rustls` configs, cert resolvers, verifiers, and
|
|
ACME state-machine wiring, transport-agnostic and shareable across
|
|
transports (noq, `tokio-rustls` TCP+TLS, and anything else that
|
|
consumes a `rustls` config). It is the extraction of the TLS handling
|
|
from alknet (alknet ADR-082/087/088 and the `crates/tls` spec are the
|
|
prior art). The conventions below apply to all work in `src/` and
|
|
`tests/`. They mirror `.opencode/agents/implementation-specialist.md`
|
|
§Project Conventions and are repeated here so they apply to every
|
|
session, not just spawned implementation agents.
|
|
|
|
1. **No comments in code** unless the user explicitly asks. This is a
|
|
project-wide convention. Doc comments (`///`, `//!`) are fine and
|
|
expected on public API. Inline `//` comments only when the user asks
|
|
or when a non-obvious security/correctness constraint would otherwise
|
|
be missed (e.g., "the root store must never be empty — a container
|
|
with no system CA bundle still needs to verify public X.509 remotes;
|
|
see alknet ADR-088 §5").
|
|
|
|
2. **Error handling** — `thiserror` for the library error type
|
|
(`TlsError`, `#[non_exhaustive]`, one variant per failure category —
|
|
the alknet ADR-088 shape). No panics in library code. No `unwrap()`
|
|
or `expect()` outside tests. If you reach for `unwrap`, the error
|
|
path wasn't specified — stop and decide what should actually happen.
|
|
For poisoned `RwLock`/`Mutex`, use `unwrap_or_else(|e| e.into_inner())`
|
|
so a panic in one operation does not cascade to other operations.
|
|
|
|
3. **`tokio` is the async runtime** — all I/O is async. The ACME state
|
|
machine is a spawned task; cert loading is sync file I/O behind an
|
|
`async fn` signature for API uniformity. Use the wasm-clean tokio
|
|
subset (`rt`, `sync`, `macros`) — **do NOT use `features = ["full"]`**
|
|
in `[dependencies]` (dev-dependencies may use `full`).
|
|
|
|
4. **The default crate should stay lean** — TLS setup and config types
|
|
only. Transport-specific wrapping is feature-gated: `noq` (the
|
|
`for_noq()` accessors), `tcp` (`tokio-rustls`), `acme` (the ACME
|
|
state machine, a heavy dep). The `rustls` dep is always present —
|
|
it is the core library. Unlike the sibling protocol crates
|
|
(alktunnels, alktty), wasm is not a load-bearing target here: the
|
|
crypto stacks (`aws-lc-rs`) and file I/O are platform code. If a
|
|
design decision ever makes the default crate heavier than "rustls +
|
|
cert material + tokio spawn," that is an ADR-worthy decision, not an
|
|
accident.
|
|
|
|
5. **Behavior-preservation invariants are load-bearing** — the extraction
|
|
from alknet must preserve these TLS behaviors exactly. An
|
|
implementation that omits any of these compiles and passes
|
|
type-checks but silently changes TLS behavior (alknet ADR-082 §
|
|
Behavior-preservation invariants):
|
|
- **`max_early_data_size = u32::MAX`** on all server config paths —
|
|
enables 0-RTT / early data. Omitting it silently breaks 0-RTT
|
|
clients.
|
|
- **`rustls::crypto::aws_lc_rs::default_provider()`** as the crypto
|
|
provider on all paths (alknet ADR-084). Do not switch to `ring` or
|
|
the process-default provider without an ADR.
|
|
- **`AcceptAnyCertVerifier`'s `supported_verify_schemes()`** returns
|
|
ED25519 + ECDSA P-256/P-384 + RSA PSS/PKCS1 (SHA256/384/512),
|
|
verbatim.
|
|
- **`acme-tls/1` ALPN append** for the ACME path only, done by the
|
|
TLS crate, not the caller (alknet ADR-027 §7).
|
|
- **Non-empty root store** — the client CA path merges
|
|
`webpki-roots` when the platform store is empty (alknet ADR-088
|
|
§5); a containerized deployment with no system CA bundle must
|
|
still verify public X.509 remotes.
|
|
|
|
6. **Fail closed** — verifier selection (fingerprint pin / CA / fail
|
|
closed) must never silently downgrade. An unknown raw-key remote
|
|
fails at handshake, not to CA verification. Identity precedence and
|
|
verifier selection follow the alknet ADR-034/ADR-091 shape: known
|
|
peer + fingerprint → pin; unknown + X.509 → CA; unknown + raw key →
|
|
fail closed. Client-auth cert presentation follows the local
|
|
identity: raw key/X.509 present the cert, `None` presents nothing,
|
|
`Acme` is a server-only identity (config error on the client path).
|
|
|
|
7. **One identity, N transports; one ACME state machine** — the central
|
|
types are `TlsServerConfig` / `TlsClientConfig`, built once and
|
|
shared (the inner `rustls` config is Clone — Arc-shared resolvers).
|
|
`TlsServerConfig` is not `Clone` (it holds the ACME task's
|
|
`JoinHandle`); share it via `Arc`. Never spawn a second ACME state
|
|
machine for a domain already being served — duplicate orders risk
|
|
Let's Encrypt rate limits and cert-cache divergence (alknet ADR-082
|
|
§The cert-reuse problem). This crate is the cert provider, not the
|
|
accept loop — transport accept loops live in the consumers
|
|
(assembly layer / endpoint crates).
|
|
|
|
8. **TLS-crate scope boundary** — this crate owns config *construction*.
|
|
Handshake-time outcomes (a rejected cert, the unknown-raw-key
|
|
fail-closed) flow through the transport's connector, not through
|
|
`TlsError` — `TlsError` is the config-construction error type (alknet
|
|
ADR-088 §6). ACME state-machine runtime errors are stream events,
|
|
logged in the spawned task, not `TlsError` variants. Do not grow
|
|
`TlsError` to cover handshake outcomes.
|
|
|
|
9. **Feature gates** — transport-specific deps are opt-in (`noq`,
|
|
`tcp`, `acme`); `default = []` — the default crate compiles lean
|
|
(ADR-003). Verify `cargo test`
|
|
(default) and `cargo test --all-features` both pass whenever features
|
|
are touched.
|
|
|
|
10. **Upstream posture** — this crate extracts working code from alknet
|
|
(`crates/alknet-tls`, `crates/alknet-core` config/fingerprint). The
|
|
alknet spec docs (ADR-082/083/084/086/087/088/089 and the
|
|
`crates/tls` README) are the reference; where this crate deviates,
|
|
record the deviation as an ADR here rather than silently diverging.
|
|
Config types (`TlsIdentity`, `Ed25519SecretKey`) are expected to
|
|
move here from `alknet-core` — this crate owns them after the
|
|
rewrite; do not re-import them from alknet.
|
|
|
|
11. **Naming** — Rust standard: `snake_case` for functions/variables/
|
|
modules, `PascalCase` for types/traits, `SCREAMING_SNAKE_CASE` for
|
|
constants.
|
|
|
|
12. **Module structure** — one module per file under `src/`, re-exported
|
|
from `src/lib.rs`. Public API surface is `lib.rs` re-exports. The
|
|
alknet shape is the reference: `server.rs` (server config +
|
|
resolvers), `client.rs` (client config + verifiers), `pem.rs`
|
|
(cert/key loading), `signing.rs` (shared signing helpers).
|
|
|
|
## Verification Commands
|
|
|
|
Run these before committing. All must pass.
|
|
|
|
```bash
|
|
cargo test # full suite
|
|
cargo clippy --all-targets -- -D warnings
|
|
cargo fmt --check
|
|
cargo doc --no-deps # if docs changed
|
|
cargo test --all-features # if features are added/touched
|
|
cargo publish --dry-run --allow-dirty # before a release
|
|
```
|
|
|
|
## Architecture Context
|
|
|
|
- `docs/research/phase-0.md` — the Phase 0 research findings (current
|
|
phase). Phase 1 will produce `docs/architecture/` (overview, ADRs,
|
|
open-questions tracker) per `docs/sdd_process.md`.
|
|
- The prior art for this crate:
|
|
- **alknet-tls** — `/workspace/@alkdev/alknet/crates/alknet-tls/` —
|
|
the working extraction source (`server.rs`, `client.rs`, `pem.rs`,
|
|
`signing.rs`); this crate supersedes it in the alknet rewrite.
|
|
- **alknet docs** — `/workspace/@alkdev/alknet/docs/architecture/`:
|
|
ADR-082 (extraction), ADR-083 (endpoint takes no TLS config),
|
|
ADR-084 (aws-lc-rs provider), ADR-027 (identity model, `acme-tls/1`),
|
|
ADR-086 (endpoint types / split ALPN lists), ADR-087
|
|
(`TlsClientConfig`), ADR-088 (`TlsError` shape, root-store
|
|
fallback), ADR-089 (client dial seam), ADR-034 (verifier
|
|
selection), ADR-091 (`ConnectionCredentials`), and
|
|
`docs/architecture/crates/tls/README.md` (the full crate spec —
|
|
the most complete reference for what this crate must do).
|
|
- Key open threads carried from alknet (Phase 0 must resolve or defer
|
|
them — see `docs/research/phase-0.md`):
|
|
- The `TlsError` in the extracted code is the simplified 3-variant
|
|
enum; the ADR-088 six-variant `#[non_exhaustive]` shape is the
|
|
recorded target and was never implemented.
|
|
- The config types (`TlsIdentity`, `Ed25519SecretKey`,
|
|
`ConnectionCredentials`) still live in `alknet-core`; where they
|
|
land for the rewrite is an alktls-side decision.
|
|
- ADR-029's fingerprint question (`029-callclient-tls-client-auth...`)
|
|
and the client-auth/remote-identity verification seams.
|
|
- If a TODO references a design direction that an ADR has since decided
|
|
against, the TODO is stale — remove it and align with the ADR. Do not
|
|
implement the rejected design. |