sync milestone-1 spec and api notes with as-built findings
This commit is contained in:
+16
-8
@@ -20,8 +20,9 @@ S1 ──> { S2, S3, S4, S5 } ──> orchestrator integration
|
||||
|
||||
- `.name = .nxdns`, `.version = "0.1.0"`, `.minimum_zig_version = "0.16.0"`, `.paths = .{""}`, `.fingerprint` (compiler suggests the value on first build — accept it).
|
||||
- Dependencies added with `zig fetch --save=<name> <url>` so the content hash is pinned:
|
||||
- `sqlite`: the newest stable SQLite **amalgamation zip** from sqlite.org (check https://sqlite.org/download.html for the current one; record the version in a comment).
|
||||
- `mbedtls`: the newest **mbedTLS 3.6.x LTS** release tarball from the Mbed-TLS GitHub releases (3.6 line only — not 4.x).
|
||||
- `sqlite`: SQLite 3.53.4 amalgamation zip (https://sqlite.org/2026/sqlite-amalgamation-3530400.zip).
|
||||
- `mbedtls`: mbedTLS 3.6.7 LTS via the GitHub **source tag** tarball (refs/tags/mbedtls-3.6.7.tar.gz). The release asset is .tar.bz2, which `zig fetch` cannot decompress; the source tarball was diffed against it — identical library + 3rdparty content (3rdparty is in-tree in the 3.6 line).
|
||||
- As built: `.gitignore` also covers `zig-pkg/` (0.16 unpacks deps there); build.zig adds `-Dgit-commit` (default "unknown") and a `run` step; mbedTLS compiles the 103 `library/*.c` files plus the five 3rdparty objects named by the everest/p256-m Makefile.inc fragments; `installHeadersDirectory` exposes mbedtls/psa headers to any module linking the lib (S4's shim needs no extra include wiring).
|
||||
|
||||
### S1.2 build.zig
|
||||
|
||||
@@ -38,7 +39,7 @@ S1 ──> { S2, S3, S4, S5 } ──> orchestrator integration
|
||||
### S1.3 src/main.zig, src/version.zig, src/tests.zig
|
||||
|
||||
- `src/version.zig`: `pub const string` / `pub const zig_version_string` pulled from `build_options`.
|
||||
- `src/main.zig`: parse first CLI arg. `version` → print version + zig version, exit 0. `run`, `check`, `export`, `import` → print `not implemented`, exit 2. No arg / unknown → usage on stderr, exit 64. No allocator gymnastics — `std.process.args` is enough here.
|
||||
- `src/main.zig`: parse first CLI arg. `version` → print version + zig version, exit 0. `run`, `check`, `export`, `import` → print `not implemented`, exit 2. No arg / unknown → usage on stderr, exit 64. Args come from `std.process.Init` (`main(init: std.process.Init)`) — `std.process.args` does not exist in 0.16; see the API notes.
|
||||
- `src/tests.zig`: `comptime { _ = @import("main.zig"); _ = @import("version.zig"); }` plus `test` block asserting the sqlite3 and mbedTLS C headers link: call `sqlite3_libversion()` and `mbedtls_version_get_string_full()` via `@cImport`-free extern declarations (declare the two extern fns manually) and check non-empty results.
|
||||
|
||||
### S1.4 Acceptance Criteria
|
||||
@@ -115,7 +116,9 @@ pub const TlsStream = struct {
|
||||
// (tls.Client holds its reader/writer by value)
|
||||
|
||||
pub const Options = struct {
|
||||
host: []const u8, // SNI + verification name
|
||||
host: []const u8, // SNI + verification name (always sent as SNI)
|
||||
// insecure_skip_verify skips CA/expiry verification but keeps SNI and leaf
|
||||
// hostname matching (stdlib couples SNI to .host = .explicit)
|
||||
ca: enum { system, insecure_skip_verify },
|
||||
// buffers supplied by caller; read_buffer.len >= std.crypto.tls.Client.min_buffer_len
|
||||
read_buffer: []u8,
|
||||
@@ -148,7 +151,8 @@ pub const TlsStream = struct {
|
||||
### S3.2 Tests
|
||||
|
||||
- Unit: `classify` mapping table (pick 6+ representative errors across the four classes).
|
||||
- Integration (compiled only when `build_options.integration`): connect to a live host (`cloudflare-dns.com:853` — DoT port, TLS without ALPN), complete the handshake with `.ca = .system`, close cleanly. This test is NOT part of the default `zig build test` run and NOT part of blocking CI.
|
||||
- Integration: lives in `src/platform/tls_client_integration_test.zig` (separate file — conditional imports are impossible in 0.16; see API notes). Runtime guard `if (!build_options.integration) return error.SkipZigTest;` — the body stays type-checked in every build, runs only under `-Dintegration`. Connects to `1.1.1.1:853` (IPv4 literal — this host's IPv6 egress is dead and the anycast address is documented) with `.host = "cloudflare-dns.com"` so SNI + hostname verification run against the real name; `.ca = .system`; clean close. NOT part of default `zig build test`, NOT part of blocking CI.
|
||||
- As built: `InitError = tls.Client.InitError || error{CertificateBundleLoadFailure}` (bundle rescan folded in, std.http.Client style). `classify` reaches exhaustiveness via `inline for` over `@typeInfo(tls.Client.InitError).error_set.?` — one mapping site, new stdlib errors break the build there.
|
||||
|
||||
### S3.3 Acceptance Criteria
|
||||
|
||||
@@ -196,8 +200,9 @@ pub const ServerStream = struct {
|
||||
};
|
||||
```
|
||||
|
||||
- Error mapping: negative mbedTLS return codes → Zig error set with named errors for the common cases (`CertParse`, `KeyParse`, `HandshakeFailed`, `PeerClosed`, `WantReadWrite` handled internally); include the raw code in a log via `mbedtls_strerror`.
|
||||
- `MBEDTLS_ERR_SSL_WANT_READ/WANT_WRITE` loop inside read/write — never surfaces to callers.
|
||||
- Error mapping: negative mbedTLS return codes → Zig error set with named errors for the common cases (`CertParse`, `KeyParse`, `KeyMismatch`, `HandshakeFailed`); include the raw code in a log via `mbedtls_strerror`.
|
||||
- `MBEDTLS_ERR_SSL_WANT_READ/WANT_WRITE` loop inside read/write — never surfaces to callers. ONLY those two codes retry; the `*_IN_PROGRESS` codes are misconfiguration (no async crypto configured) and surface as errors.
|
||||
- As built (review round 1): only an authenticated close_notify reads as clean `error.EndOfStream`; transport EOF without close_notify → `error.ReadFailed` with `read_err = .TlsConnectionTruncated` (no opt-out). `ServerContext.init` calls `mbedtls_pk_check_pair` (via the `nx_x509_crt_pk` shim accessor) → `error.KeyMismatch` on a mismatched pair, covered by the `tests/fixtures/mismatched_key.pem` fixture. mbedTLS compiles with `MBEDTLS_THREADING_C` + `MBEDTLS_THREADING_PTHREAD` (build.zig `addMbedtlsThreadingMacros`, applied to every module that sees mbedTLS headers — context sizes change with threading).
|
||||
|
||||
### S4.3 Test fixture + loopback test
|
||||
|
||||
@@ -254,6 +259,8 @@ src/platform/tls_server.zig S4 mbedTLS server wrapper
|
||||
src/platform/mbedtls_shim.c S4 sizeof shims
|
||||
tests/fixtures/self_signed_cert.pem S4
|
||||
tests/fixtures/self_signed_key.pem S4
|
||||
tests/fixtures/mismatched_key.pem S4 (review round 1: KeyMismatch fixture)
|
||||
tests/fixtures/fixtures.zig S4 (@embedFile module root for fixtures)
|
||||
tests/fixtures/README.md S4
|
||||
.gitea/workflows/ci.yml S5
|
||||
.gitea/workflows/live-tls.yml S5
|
||||
@@ -274,7 +281,8 @@ S2–S5 MUST NOT edit build.zig, build.zig.zon, or src/tests.zig. If a session n
|
||||
## Acceptance Criteria (Milestone 1 Complete)
|
||||
|
||||
- [ ] `zig build test` exits 0 (aggregator includes address, tls_client unit, tls_server unit tests).
|
||||
- [ ] `zig build test -Dintegration` exits 0 locally (loopback TLS echo; live DoT test may be skipped on network failure with a visible skip message).
|
||||
- [ ] `zig build test -Dintegration` exits 0 locally (hermetic loopback TLS echo; deterministic, PR-blocking in CI).
|
||||
- [ ] `zig build test -Dintegration -Dlive` exits 0 locally (adds the live DoT handshake; manual workflow only — a live-network failure is an environment finding, not a gate).
|
||||
- [ ] `zig build cross` produces two statically linked executables that print `nxdns version` output under qemu-user or on-target (checked manually for aarch64 if qemu absent).
|
||||
- [ ] CI workflows valid YAML; `test` + `cross` jobs green on the Gitea runner.
|
||||
- [ ] All files committed with GPG-signed, lowercase-message commits.
|
||||
|
||||
Reference in New Issue
Block a user