repo scaffold + AGENTS.md + Phase 0 research
- Cargo scaffold: feature gates (quinn/tcp/acme), lean tokio subset, placeholder lib; Cargo.lock committed with time pinned to 0.3.36 so rust-version = 1.85 is actually satisfiable (rcgen's default time resolution requires 1.88 — alknet-tls fails the same check) - AGENTS.md adapted from alktunnels: TLS-crate conventions (behavior- preservation invariants, fail-closed verifier selection, one ACME state machine, config-construction scope boundary, no wasm target) - .opencode/agents: implementation-specialist conventions + coordinator prompt template + architect deferral examples updated for alktls - docs/research/phase-0.md: extraction inventory with verified invariants (line-referenced), spec-vs-code gaps (TlsError shape, for_tcp_tls, config-type ownership), rewrite requirements, OQ-TLS-01..07, MSRV verification record Verified: cargo test, clippy -D warnings, fmt --check, doc --no-deps, test --all-features, rustup run 1.85 cargo check
This commit is contained in:
@@ -0,0 +1,209 @@
|
||||
# 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 (quinn, `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: `quinn` (the
|
||||
`for_quinn()` 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 (`quinn`,
|
||||
`tcp`, `acme`). The base crate compiles lean. 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.
|
||||
Reference in New Issue
Block a user