| name | freenet-mail-release |
| description | Production release runbook for freenet-email. Use when the user asks to "cut a release", "release v0.X.Y", "publish to production", or after a non-trivial bug sweep that should reach users. Source of truth is RELEASING.md. |
Freenet Mail Release
When to invoke
- User says: "cut a release", "release v0.X.Y", "publish to production",
"do the release", "release.sh", "tag a new version".
- A bug-fix sweep just landed on
main and the user asks whether to
ship it.
- A new feature merged with user-facing behavior that warrants a tag.
Do NOT invoke for: facade-only pointer updates without webapp changes
(those are a separate flow; see RELEASING.md ยง"Facade contract
update"), test/CI-only commits, doc-only commits.
What you can do vs. what only the user can do
You CAN:
- Audit commits since the last tag (
git log v0.1.X..main).
- Verify preflight requirements: clean tree, tag availability,
production key file presence, GNU tar (
gtar), fdev, dx,
cargo-make, gh.
- Confirm a
freenet network node is reachable on 127.0.0.1:7509
(or whatever FREENET_PORT overrides to).
- Draft release notes from the commit log.
- Run the post-release smoke test (
scripts/smoke-test-production.sh).
- Verify the facade pointer flip post-publish (curl probes).
You CANNOT:
- Run
scripts/release.sh itself unsupervised. It signs with the
production key, pushes to the live network, commits + tags + pushes
to origin/main. Steps 4-10 are irreversible. The user must drive
this, or explicitly authorize each confirmation.
- Start
freenet network. The node is a long-running interactive
process whose output the user should be watching during publish.
- Generate or rotate the production key. That lives at
~/.config/freenet-email/web-container-keys.toml and is the user's
asset.
Preflight checklist
Run these checks before suggesting release.sh:
git status
git rev-parse --abbrev-ref HEAD
git fetch origin main && git status
git tag | grep "^v<VERSION>$" && echo "TAG TAKEN" || echo "TAG OK"
git log $(git describe --tags --abbrev=0)..main --oneline
ls -la ~/.config/freenet-email/web-container-keys.toml
which gtar || (tar --version | grep -q GNU && echo "system tar is GNU") || \
echo "MISSING gtar โ `brew install gnu-tar`"
curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:7509/
which fdev dx cargo-make gh
Report any failures as a punch list before the user runs release.sh.
Do not paper over them.
Picking the version
Semver per Cargo.toml:
- Patch bump (
v0.1.X โ v0.1.X+1): bug fixes only. The recent
228+ sweep was a patch.
- Minor bump (
v0.1.X โ v0.2.0): user-visible features, schema
additions, new contracts.
- Major bump: protocol stabilizes (we're not there yet).
Confirm the version with the user before suggesting; do not pick
unilaterally.
Running the release
The user runs:
freenet network
scripts/release.sh <VERSION>
scripts/smoke-test-production.sh
release.sh prompts at:
- Before publish (irreversible step starts).
- After publish, before commit + tag.
- Before push to origin.
If the user wants you to type "y" at each prompt, they should pass
--yes to release.sh and confirm authorization explicitly. Don't
assume.
Post-release verification
After release.sh exits successfully (or if it aborted post-publish โ
see "Recovery" below), verify the deploy actually works:
scripts/smoke-test-production.sh
NEW_APP_ID=$(cat published-contract/contract-id.txt)
FACADE_ID=$(cat published-contract/facade-id.txt)
curl -s "http://127.0.0.1:7509/v1/contract/web/${FACADE_ID}/?__sandbox=1" \
| grep -F "${NEW_APP_ID}"
curl -sI "http://127.0.0.1:7509/v1/contract/web/${NEW_APP_ID}/" | head -1
If facade ?__sandbox=1 shows the OLD app id, the gateway is serving
a stale extracted webapp from its on-disk cache. See AGENTS.md
ยง"Manual pointer flip / recovery" for the cache-bust recipe.
Recovery
release.sh aborts during publish (fdev 300s timeout)
freenet-core#4102: fdev publish returns a 300s client timeout despite
the server-side publish having succeeded. The webapp landed; the
facade pointer didn't flip. Recipe lives in RELEASING.md ยง"Manual
pointer flip"; the key facts:
fdev execute update needs --as-state, not the default
UpdateData::Delta (the facade contract silently rejects Delta as
InvalidUpdate). See memory fdev_update_needs_as_state.md.
- The facade
web slot must be an xz-tar containing index.html, not
raw HTML. cargo make sign-facade-state (which chains
pack-facade-loader) gets this right; do not skip it. See memory
facade_state_framing.md.
Smoke test fails post-publish
The webapp is live but broken. Options:
- Roll forward: fix the bug, bump the version, release again.
- Temporary mitigation: point users at the previous contract id
(which is still served by the network). There is no formal
rollback.
Do not attempt to delete or overwrite the bad contract โ Freenet
contracts are immutable; only the facade pointer can be flipped, and
only forward.
Production key lost
Generate a new one with scripts/generate-production-key.sh. The new
contract id will differ; every existing user must re-point their
client. Announce the rotation. Treat this as a last-resort recovery
path, not a routine action.
Anti-patterns
- Don't run
release.sh from a dirty tree or a non-main branch.
The script refuses, but if forced via a flag, the resulting tag
points at non-main state and is unrecoverable.
- Don't skip the smoke test. The publish pipeline has multiple
silent-failure modes (corrupted tar, wrong signature, stale id);
the smoke test is the only thing that catches them before users do.
- Don't push a release that only contains test or CI changes. The
contract id rotates anyway because workspace
Cargo.lock churn
shifts wasm bytes (#198) โ burning a version on a no-op release
wastes a bookmark migration for users.
- Don't edit
published-contract/ by hand. Every committed file
there is produced by update-published-contract-prod (or the facade
equivalent) and signed with the production key.
- Don't commit
target/facade/facade.state. Per-release signed
artifact; only the WASM/parameters/id snapshot under
published-contract/facade.* is tracked.
Cross-references
RELEASING.md โ full runbook with recovery and facade details.
AGENTS.md ยง"Publishing" โ facade architecture, framing, lockfile
isolation.
- Memory
fdev_update_needs_as_state.md โ flag required for facade
UPDATEs.
- Memory
facade_state_framing.md โ web slot framing requirement.
- Memory
test_vs_prod_snapshot.md โ why the test snapshot drifts
between test and prod publishes.