carlos / contra Public

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
Paul Campbell pushed by paul@keymail.dev 361cff88978e6d0eb0ba050467420a82ee6131df parent 71fe556
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 −0
  • Makefile +109 −0
  • cmd/contra/main.go +62 −0
  • go.mod +30 −2
  • go.sum +93 −0
  • internal/contra/app.go +61 −0
  • internal/{ => contra}/crypto/aead.go +0 −0
  • internal/{ => contra}/crypto/code.go +0 −0
  • internal/{ => contra}/crypto/ec.go +0 −0
  • internal/{ => contra}/crypto/kdf.go +0 −0
  • internal/{ => contra}/crypto/vectors_test.go +1 −1
  • internal/contra/handlers.go +24 −0
  • internal/contra/icons/icons.go +47 −0
  • internal/contra/migrations.go +38 −0
  • internal/contra/models.go +18 −0
  • internal/contra/render.go +72 −0
  • internal/contra/static/datetime.js +1338 −0
  • internal/contra/static/rastrillo.js +337 −0
  • internal/contra/static/select.js +280 −0
  • internal/contra/static/theme.css +180 −0
  • internal/contra/static/tokens.css +1664 −0
  • internal/contra/templates/errors.html +9 −0
  • internal/contra/templates/index.html +28 −0
  • internal/contra/templates/layout.html +111 −0
  • internal/contratest/browser_test.go +53 −0
  • internal/contratest/harness_test.go +62 −0
  • internal/contratest/index_test.go +71 −0
  • internal/contratest/storage_test.go +151 −0
  • internal/contratest/vendored_test.go +54 −0
  • manifest/README.md +52 −0
  • vectors/README.md +1 −1
.amadan/ci

Loading diff…

.amadan/ci.d/10-vet

Loading diff…

.amadan/ci.d/20-fmt

Loading diff…

.amadan/ci.d/30-test

Loading diff…

.amadan/ci.d/40-migration-check

Loading diff…

.gitignore

Loading diff…

Makefile

Loading diff…

cmd/contra/main.go

Loading diff…

go.mod

Loading diff…

go.sum

Loading diff…

internal/contra/app.go

Loading diff…

internal/{ => contra}/crypto/aead.go

Loading diff…

internal/{ => contra}/crypto/code.go

Loading diff…

internal/{ => contra}/crypto/ec.go

Loading diff…

internal/{ => contra}/crypto/kdf.go

Loading diff…

internal/{ => contra}/crypto/vectors_test.go

Loading diff…

internal/contra/handlers.go

Loading diff…

internal/contra/icons/icons.go

Loading diff…

internal/contra/migrations.go

Loading diff…

internal/contra/models.go

Loading diff…

internal/contra/render.go

Loading diff…

internal/contra/static/datetime.js

Loading diff…

internal/contra/static/rastrillo.js

Loading diff…

internal/contra/static/select.js

Loading diff…

internal/contra/static/theme.css

Loading diff…

internal/contra/static/tokens.css

Loading diff…

internal/contra/templates/errors.html

Loading diff…

internal/contra/templates/index.html

Loading diff…

internal/contra/templates/layout.html

Loading diff…

internal/contratest/browser_test.go

Loading diff…

internal/contratest/harness_test.go

Loading diff…

internal/contratest/index_test.go

Loading diff…

internal/contratest/storage_test.go

Loading diff…

internal/contratest/vendored_test.go

Loading diff…

manifest/README.md

Loading diff…

vectors/README.md

Loading diff…