Battle tested, with the boundaries visible.
Trestle treats hardening as an attempt to falsify explicit guarantees. This page records what each campaign attacked, what passed, and what remains outside the evidence.
A green campaign establishes behavior for the workloads and failure classes exercised. It is not a security certification and does not manufacture years of field exposure.
Evidence status
| Campaign | Status | Evidence |
|---|---|---|
| 23.1 Contracts and threat model | Complete | Trust boundaries, proxy spoof tests, redacted diagnostics, six release targets, checksum-verified update and rollback. |
| 23.2 Input fuzzing | Complete | 348,376 generated executions across filters, rules, webhook URLs and Lambda targets without panic or invariant failure. |
| 23.3 Concurrency and failure injection | Complete | 64 contended jobs executed once; expired leases recovered; 20 race-enabled repetitions passed across queue, store and record packages. |
| 23.4 Migration and upgrade matrix | Complete | All 14 schema starting points upgraded five times with preserved data, complete history and SQLite integrity; injected failure rolled back. |
| 23.5 Recovery drills | Complete | Five database/file round trips plus corrupt, truncated, incomplete, future-schema, traversal and occupied-target failures; no partial target published. |
| 23.6 Authorization abuse | Complete | Cross-owner and token/scope confusion matrix repeated ten times plus race suites; one individual row-rule bypass found and repaired. |
| 23.7 Load and resource bounds | Complete | 4,800 concurrent reads, explicit page/batch rejection and ten TCP slow-header trials; bounded responses and no retained goroutines. |
| 23.8 Browser quality | Complete | Retained accessibility, keyboard, responsive, strict-CSP and embedded asset-budget checks. |
| 23.9 Contract truth | Complete | Production configuration, module checksums, stale claims and all 80 generated documentation pages audited. |
| 23.10 Release candidate | Complete | Complete retained matrix, six verified archives, install, update, rollback and clean quickstart. |
23.1 - Baseline contracts
The initial campaign mapped public HTTP, administrator sessions, bearer identities, SQLite, object storage, automation egress, release downloads and backup archives as separate trust boundaries. Tests prove that untrusted forwarded headers do not become trusted client identity, support bundles exclude secrets, and release replacement verifies the selected archive checksum before atomic installation.
The updater preserves one previous binary for explicit rollback and does not read or write Trestle instance data. This establishes release-file behavior, not database downgrade compatibility; restoration and rollback drills belong to 23.5.
23.2 - Adversarial input mutation
Four retained Go fuzz targets attack the typed filter parser/compiler, access-rule validator/evaluator, webhook target URLs and Lambda target validation. The campaign included valid seeds, injection-shaped values, private and credential-bearing URLs, malformed ARNs, oversized expressions and generated mutations.
Across 348,376 executions, accepted filters retained one SQL placeholder per bound argument, invalid rules never authorized, accepted webhook URLs retained the HTTPS/no-credentials contract, and target validators returned without panic. This is scoped mutation evidence, not proof over every input and not an authorization penetration test.
23.3 - Concurrency, interruption and restart
Eight workers contended for 64 durable SQLite jobs. Every job reached succeeded with exactly one recorded attempt and one executor call. A separately seeded running job with an expired lease modeled process interruption; the next worker returned it to pending, claimed it and completed its second attempt.
The queue, store and full record workflow then ran 20 times under Go's race detector. This attacks duplicate claims, shared handler state, SQLite serialization, optimistic mutations and restart recovery under the schedulers exercised here. It cannot enumerate every interleaving, kernel, filesystem or power-loss boundary.
23.4 - Every retained schema boundary
The migration matrix constructs a database at every retained schema state from version 0 through version 13, seeds durable data wherever the system table exists, and reopens it through Trestle's production initializer. Each path must reach the current version with all 13 migration records, the probe unchanged and PRAGMA integrity_check reporting ok.
All 14 paths passed five repetitions. A deliberately invalid version-7 migration starting from version 6 created a table and then failed inside one transaction; the table disappeared, the database remained at version 6 and prior data survived. A version-999 database remains explicitly refused. These are schema-fixture results; final release-binary and cross-platform upgrade compatibility remains part of the complete CP23 matrix.
23.5 - Restore what the backup claims to protect
A real authenticated backup containing a seeded SQLite row and local file object was restored into a new owner-only data directory five times. Trestle reopened the restored database through its production initializer, verified SQLite integrity, and recovered both probes byte-for-byte.
The restore boundary then received traversal and backslash paths, a missing database, a corrupt database, a future schema, a truncated ZIP and an already occupied target. Every case failed before the staged directory was atomically published. The release updater's update -> rollback drill separately restored the prior executable without opening instance data. Local objects are covered; S3 recovery remains the operator's provider-level backup/versioning responsibility.
23.6 - Attack authority across interfaces
The composed abuse matrix creates two application users, scoped service identities, independent owner rows and per-operation collection rules. It attacks filtered lists, direct record lookup, cross-owner update/delete, forged ownership on create, refresh-token use as an access token, read-scope mutation and browser-versus-bearer mutation semantics.
The first run found a real authorization defect: an individual owner rule was evaluated but a false result did not stop view/update/delete when the record ID was known. The shared decision point now returns the same 404 as an absent record while list rules retain SQL filtering. Ten repaired matrix repetitions and the race-enabled identity, rules, records, files and server suites passed. This is an engineering abuse corpus, not an independent penetration test.
23.7 - Bounded work under pressure
The load corpus seeded 100 typed records and issued 1,200 list requests from 24 concurrent workers. Three ordinary runs and one race-enabled run completed 4,800 requests with bounded response bodies, no failures and no retained goroutine growth. Oversized page and 1,001-record batch requests failed at their documented boundaries before expanding into database work.
A real TCP client then sent an incomplete HTTP header more slowly than the configured header timeout. Ten repetitions were disconnected before the request reached an application handler. These measurements establish behavior for this local machine and workload; they are not a universal requests-per-second or capacity promise.
23.8 - Browser quality is a contract
The embedded Nift dashboard is checked for document language, named navigation, route focus, current-page semantics, announced connection state, visible keyboard focus, Escape-to-close mobile navigation, reduced-motion handling and long-value containment. Trestle also emits a strict content security policy with no inline script or style dependency.
Performance remains explicit: generated HTML must stay below 24 KiB, CSS below 96 KiB and the complete three-file embedded dashboard below 320 KiB. These structural checks do not replace testing by people using assistive technology, but they prevent common regressions from silently shipping.
23.9 - Documentation must match the executable
The retained contract audit extracts every production TRESTLE_* configuration key from source and requires it to appear in the README or public documentation. It rejects stale hardening status, prohibited comparison wording and em dashes, verifies the recorded Go module graph, and resolves local links across all 80 generated HTML pages and 84 public files.
The audit corrected stale pre-hardening status and upgrade guidance before this checkpoint closed. It proves consistency with this commit, not that a future implementation change can skip review; the script exists so those changes fail visibly.
23.10 - One release-candidate gate
The final gate ran normal and race-enabled Go tests, vet, browser and contract audits, migration and recovery drills, authorization abuse, bounded load, concurrency stress, four fuzz targets, six cross-platform packages, checksum and archive verification, user installation, executable update and rollback, and a clean local launch.
Every available gate passed. Docker was unavailable in the execution environment, so trusted-proxy unit tests ran ten times instead of repeating the containerized Caddy/nginx smoke test from CP22. Completing this matrix creates a release candidate; it is not a stable tag, independent audit or substitute for field history.
PostgreSQL campaign evidence
The PostgreSQL parity campaign is separate from the CP23 release-candidate evidence. SQLite and PostgreSQL are both first-class database options. This section records only what has been exercised against a real server.
- Real providers tested: PostgreSQL 18.6 on disposable non-superuser databases locally, and PostgreSQL 16, 17 and 18 services in the CI matrix running the normal and race suites.
- PG04 identity parity: competing first-admin setup creates exactly one administrator; administrator and application-user sessions, refresh/access tokens, service accounts and personal tokens run the same provider-parameterized suite on SQLite and PostgreSQL.
- PG05 schema parity: all nine field types map to reviewed provider DDL; rename and additive changes preserve stable columns; incompatible schema changes fail closed with old metadata and rows intact; JSON is validated at the database level on both providers.
- PG06 record parity: create/get/update/delete, 1,000-record atomic batches, idempotency, projections, optimistic concurrency and boolean filters behave identically; concurrent updates and idempotency races have exactly one winner.
- PG07 query parity: null, number, datetime and case-insensitive contains semantics, sort direction, filter+cursor pagination and typed error mapping match on both providers.
- PG08 access-rule and file-metadata parity: the authorization abuse matrix (row filtering, non-leaking denial, scope enforcement, service and personal credentials) and the file lifecycle suite (upload, range, quota, symlink rejection, orphan cleanup) run unchanged on SQLite and PostgreSQL; rule storage and administrator simulation match on both providers.
- PG09 events, audit, jobs and integration parity: durable event sequencing and gap-tolerant replay, authorized realtime visibility, audit facts with redaction and filtering, and the job engine with deliberate per-provider claiming all run the same provider-parameterized suites; eight concurrent workers claim 64 jobs with exactly one execution each on SQLite and PostgreSQL, and rolled-back mutations publish no event, audit fact, webhook job or Lambda job.
- PG10 backup, restore and operations parity: the portable logical archive round-trips in all four directions (SQLite to/from PostgreSQL), native SQLite snapshots are retained, logical archives restore into empty destinations with all-or-nothing publication, and the operations summary reports provider size facts; failures leave the target untouched on both providers.
- PG11 migration, parity matrix and Incident Desk: offline migration round-trips both directions (SQLite to/from PostgreSQL) with read-only sources, explicit confirmation, dry-run, semantic destination verification and a checksum; the provider-parameterized product matrix runs on both engines; Incident Desk runs unchanged against both SQLite and PostgreSQL 18.6 (auth, records, files and realtime smoke passes on each).
- Composed gate and availability: the CI workflow runs format, vet, frontend checks, all six cross-builds, release packaging/installer/update checks, and real Caddy and nginx TLS smokes against the same committed state; a completely green composed run closes the availability gate. PostgreSQL remains one external API contract with provider-specific internals; managed/serverless topologies and deeper provider metrics remain recorded limitations.
How to read this page
Trestle separates input, concurrency, migration, recovery, security, load, interface and field evidence. A result in one category does not silently become a claim in another. Findings become retained regression tests, and this page changes only when the corresponding evidence has run.