web: bootstrap the instance, the gate, and something to deploy
Nothing else in the web track can land cleanly until this exists, and the sharpest reason is the merge gate: AGENTS.md wants "done, approved, CI green", and with no CI script there was no green anybody could reach. That is what .amadan/ci and its four steps close. Each step execs a make target rather than carrying its own copy of the commands, following amadan's own rule that debugging CI is running it — `make ci` locally is byte-for-byte what the runner does. Scaffolded with `rastrillo new --shell=console`, not hand-rolled: the framework's packages exist to be used, and rastrillo/db already sets busy_timeout before journal_mode, WAL, foreign keys and a single-connection writer pool — exactly what web/1 asks for and exactly the ordering that crashes under concurrent open when you get it backwards. Two departures from the scaffold, both deliberate. The example `notes(title, body)` model is gone rather than renamed. In a host-blind password manager a plaintext table is not a starting point to edit; the whole SQLite file replicates to object storage, so it is the shape the one rule forbids. The app therefore owns no tables at all yet, and the sealed schema arrives with the merciless greps in web/2 or it does not arrive. TestAppOwnsNoTablesYet fails the moment a table appears, and says why in its message — a placeholder for those greps, to be replaced by them rather than deleted. sessions.Schema is in BootSchema from this first migration even though sign-in is web/3's. Migrations are additive-only and the first deploy of a version carrying them has to be schema-neutral; getting the session tables into 0001 costs nothing today and is the case the framework refuses later. The storage tests assert the pragmas rather than trusting the framework to have set them. "The framework does it" is a claim; PRAGMA journal_mode is a fact, and the two are worth keeping distinguishable. internal/crypto moves to internal/contra/crypto: `rastrillo migration check` requires exactly one app package under internal/, and keeping that check in CI is worth more than the old path. The landing page's prose is lifted verbatim from README.md. Copy in this repo is a reviewed artifact and the canary needed a page before that review could happen, so it says only what the README already says.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hz8mMZYs75WuRC1hYpmapJ
36 files changed,
+4969
−4
.amadan/ci+6 −0.amadan/ci.d/10-vet+2 −0.amadan/ci.d/20-fmt+2 −0.amadan/ci.d/30-test+2 −0.amadan/ci.d/40-migration-check+2 −0.gitignore+9 −0Makefile+109 −0cmd/contra/main.go+62 −0go.mod+30 −2go.sum+93 −0internal/contra/app.go+61 −0internal/{ => contra}/crypto/aead.go+0 −0internal/{ => contra}/crypto/code.go+0 −0internal/{ => contra}/crypto/ec.go+0 −0internal/{ => contra}/crypto/kdf.go+0 −0internal/{ => contra}/crypto/vectors_test.go+1 −1internal/contra/handlers.go+24 −0internal/contra/icons/icons.go+47 −0internal/contra/migrations.go+38 −0internal/contra/models.go+18 −0internal/contra/render.go+72 −0internal/contra/static/datetime.js+1338 −0internal/contra/static/rastrillo.js+337 −0internal/contra/static/select.js+280 −0internal/contra/static/theme.css+180 −0internal/contra/static/tokens.css+1664 −0internal/contra/templates/errors.html+9 −0internal/contra/templates/index.html+28 −0internal/contra/templates/layout.html+111 −0internal/contratest/browser_test.go+53 −0internal/contratest/harness_test.go+62 −0internal/contratest/index_test.go+71 −0internal/contratest/storage_test.go+151 −0internal/contratest/vendored_test.go+54 −0manifest/README.md+52 −0vectors/README.md+1 −1
Intent
.amadan/ciLoading diff…
.amadan/ci.d/10-vetLoading diff…
.amadan/ci.d/20-fmtLoading diff…
.amadan/ci.d/30-testLoading diff…
.amadan/ci.d/40-migration-checkLoading diff…
.gitignoreLoading diff…
MakefileLoading diff…
cmd/contra/main.goLoading diff…
go.modLoading diff…
go.sumLoading diff…
internal/contra/app.goLoading diff…
internal/{ => contra}/crypto/aead.goLoading diff…
internal/{ => contra}/crypto/code.goLoading diff…
internal/{ => contra}/crypto/ec.goLoading diff…
internal/{ => contra}/crypto/kdf.goLoading diff…
internal/{ => contra}/crypto/vectors_test.goLoading diff…
internal/contra/handlers.goLoading diff…
internal/contra/icons/icons.goLoading diff…
internal/contra/migrations.goLoading diff…
internal/contra/models.goLoading diff…
internal/contra/render.goLoading diff…
internal/contra/static/datetime.jsLoading diff…
internal/contra/static/rastrillo.jsLoading diff…
internal/contra/static/select.jsLoading diff…
internal/contra/static/theme.cssLoading diff…
internal/contra/static/tokens.cssLoading diff…
internal/contra/templates/errors.htmlLoading diff…
internal/contra/templates/index.htmlLoading diff…
internal/contra/templates/layout.htmlLoading diff…
internal/contratest/browser_test.goLoading diff…
internal/contratest/harness_test.goLoading diff…
internal/contratest/index_test.goLoading diff…
internal/contratest/storage_test.goLoading diff…
internal/contratest/vendored_test.goLoading diff…
manifest/README.mdLoading diff…
vectors/README.mdLoading diff…