release: 1.0.0: Public 1.0 image; delivery log; docs and SECURITY.md (incl. 1.0.1)
[1.0.0]
Added:
- A **Delivery log** on each delivery's page (`/deliveries/{id}`): the
- A **History** block on the same page: the journal's two timestamps stated as
Fixed:
- Postfix config: copy TLS cert/key from the (often `:ro`, host-owned) mount
- Postfix config: allow `maillog_file` under `/data` via
- E2e: read `/data/setup-token` via `docker compose exec` (file is `0600`
- Entrypoint: check `SELFPOST_HOSTNAME` before `/data` setup (so a bad identity
- E2e gate: wait for host-published `/healthz` before panel setup, and stop
… (3 more in CHANGELOG)
Changed:
- Document what `.spbk` and `.spde` stand for (SelfPost backup / SelfPost domain
- Docs aligned with the code: setup URL is `/setup/<token>` (README and
- Full backups no longer carry `/data/log`. It is Postfix's raw log plus its
- Monitoring screens (status, mail queue, system log, deliveries) use adaptive
- Documentation package consolidated into `docs/development.md`: Documentation
… (4 more in CHANGELOG)
[1.0.1]
A documentation and packaging release: no change to the mail path, the database, or the on-disk layout. Upgrading is a tag bump.
Added:
- `SECURITY.md` — how to report a vulnerability privately (GitHub private
- [docs/plans/](docs/plans/) — one document per agreed extension: the optional
Changed:
- The panel's **Account** entry is now called **Settings** — nav link, page
- The signed-in name in the panel's nav is now labelled `User:`, so it reads as
- [docs/product.md](docs/product.md) reframes the future line: the inbound
- [docs/security.md](docs/security.md) is now in English, matching the rest of
- The README documentation table now points at `SECURITY.md` for reporting a
… (2 more in CHANGELOG)
Fixed:
- The panel's static assets are served with a content ETag and
Squashed CHANGELOG sections: [1.0.0], [1.0.1].
Full detail: CHANGELOG.md (1.0.0, 1.0.1).
This commit is contained in:
+314
-103
@@ -1,146 +1,357 @@
|
||||
# SelfPost — development
|
||||
|
||||
**What this file is.** How to build, test, and ship changes. Current sprint
|
||||
state lives in [progress.md](progress.md) — read that first after `/clear`.
|
||||
|
||||
Product boundaries: [product.md](product.md). As-built layout:
|
||||
**What this file is.** How to build, test, document, and ship changes. Open
|
||||
work after 1.0 (1.x+) lives in [roadmap.md](roadmap.md) and linked
|
||||
[plans/](plans/). Product boundaries: [product.md](product.md). As-built layout:
|
||||
[architecture.md](architecture.md).
|
||||
|
||||
---
|
||||
|
||||
## Repository layout
|
||||
## Resuming work
|
||||
|
||||
- `cmd/panel` — panel HTTP server + milter + log-tailer
|
||||
- `cmd/selfpost-backup` — CLI backup (`docker exec … selfpost-backup`)
|
||||
- `internal/` — domain logic, store, web handlers, health checks
|
||||
- `build/` — Dockerfile, supervisord, Postfix/OpenDKIM wiring, entrypoint
|
||||
After `/clear` or a fresh chat:
|
||||
|
||||
1. Read this file (process, docs rules, model routing).
|
||||
2. Open [roadmap.md](roadmap.md) for the index of open work; follow the linked
|
||||
plan file for the active item. Accepted risks — [security.md](security.md);
|
||||
as-built — [architecture.md](architecture.md).
|
||||
3. Skim [product.md](product.md) if scope is in doubt.
|
||||
4. Continue from the next unchecked step in the **active** plan file (not the
|
||||
roadmap index).
|
||||
|
||||
History of closed phases is in `git log` and [CHANGELOG.md](../CHANGELOG.md),
|
||||
not duplicated here.
|
||||
|
||||
---
|
||||
|
||||
## Authorship and disclosure
|
||||
|
||||
**Decided, not open for re-litigation.** SelfPost is written by AI agents under
|
||||
a maintainer's direction, and the project says so rather than hiding it.
|
||||
|
||||
Concretely, this is what "says so" means, and none of it is an oversight to be
|
||||
tidied away later:
|
||||
|
||||
- `Co-Authored-By: Claude <model>` trailers stay in commit messages, including
|
||||
the ~140 commits that predate v1.0.
|
||||
- The model routing table below is public, in a file the README links to.
|
||||
- [.cursor/rules/agent-rules.mdc](../.cursor/rules/agent-rules.mdc) ships in
|
||||
the repository.
|
||||
- Process notes written for an agent — "after a context reset, pick an item
|
||||
marked `agreed`" in [roadmap.md](roadmap.md) — stay as they are.
|
||||
|
||||
**Why not quietly drop it.** Once the trailers are in the history, removing the
|
||||
routing table or the rules file would not conceal authorship, it would only
|
||||
make the project look like it was trying to. Partial concealment reads worse
|
||||
than the plain statement, and the plain statement costs nothing: the code is
|
||||
reviewed, tested, and shipped under the same rules either way, and the
|
||||
[security design](security.md) records what was audited and what was accepted.
|
||||
|
||||
**Revisit if:** the disclosure ever conflicts with the licence or a downstream
|
||||
obligation — not because the convention around AI authorship shifts.
|
||||
|
||||
---
|
||||
|
||||
## Model routing
|
||||
|
||||
| Kind of work | Model | Examples |
|
||||
|---|---|---|
|
||||
| Security, infra, file permissions, Postfix/`postqueue`, open-relay risk | **Opus** | `mail.log` under `/data`, entrypoint permissions, queue reconcile |
|
||||
| UI / JS / CSS, templates, documentation (English), README | **Sonnet** | adaptive polling, this file's Documentation section |
|
||||
| Trivial mechanics: retarget links, grep, compose bump, CHANGELOG cut | **Haiku** | Makefile / release.yml comment fixes, deleting closed plan files |
|
||||
| Security **review** (not authorship) | **Fable** | pre-release checklist (CHANGELOG `[0.5.0]` Security / [security.md](security.md) — done) |
|
||||
|
||||
Default rule: risk-critical → Opus; UI / docs / boilerplate → Sonnet; trivial
|
||||
mechanics → Haiku. Reviewers must not be the author of the code under review.
|
||||
|
||||
---
|
||||
|
||||
## Technology stack and tools
|
||||
|
||||
| Component | Version / notes |
|
||||
|---|---|
|
||||
| **Go** | 1.26+ (`go.mod`); `CGO_ENABLED=0` — pure Go, static linking |
|
||||
| **SQLite** | `modernc.org/sqlite` (pure Go, no cgo) |
|
||||
| **Build** | [Makefile](../Makefile): primary targets `vet`, `test`, `build`, `e2e` (also `all`, `clean`) |
|
||||
| **Container** | Docker + Compose v2 on the dev host and in CI |
|
||||
| **Image (build stage)** | `golang:1.26-bookworm` — [build/Dockerfile](../build/Dockerfile) |
|
||||
| **Image (runtime)** | `debian:bookworm-slim` + Postfix, OpenDKIM, supervisord, SASL, logrotate |
|
||||
| **CI** | GitHub Actions — [.github/workflows/](../.github/workflows/) |
|
||||
| **Image registry** | `ghcr.io/mixeme/selfpost` |
|
||||
|
||||
**Repository layout** (brief; process details in [architecture.md](architecture.md)):
|
||||
|
||||
- `cmd/panel` — HTTP panel + journal-milter + log-tailer
|
||||
- `cmd/selfpost-backup` — backup CLI (`docker exec … selfpost-backup`)
|
||||
- `internal/` — domain logic, store, web, health
|
||||
- `build/` — Dockerfile, supervisord, Postfix/OpenDKIM, entrypoint
|
||||
- `deploy/` — `docker-compose.yml`, proxy examples, `.env.example`
|
||||
- `test/e2e/` — **separate Go module**; container integration tests
|
||||
|
||||
---
|
||||
|
||||
## Local Go workflow
|
||||
## External libraries
|
||||
|
||||
Requires Go 1.26+ and `CGO_ENABLED=0` (pure Go SQLite).
|
||||
The project is **AGPL-3.0** ([LICENSE](../LICENSE)). New Go dependencies must
|
||||
be permissive or GPL-family (see
|
||||
[.cursor/rules/agent-rules.mdc](../.cursor/rules/agent-rules.mdc)).
|
||||
|
||||
### Main module (`go.mod`)
|
||||
|
||||
| Package | Version | Repository | License |
|
||||
|---|---|---|---|
|
||||
| `github.com/emersion/go-milter` | v0.4.1 | <https://github.com/emersion/go-milter> | BSD-2-Clause |
|
||||
| `golang.org/x/crypto` | v0.54.0 | <https://github.com/golang/crypto> | BSD-3-Clause |
|
||||
| `modernc.org/sqlite` | v1.53.0 | <https://gitlab.com/cznic/sqlite> (mirror: <https://github.com/modernc-org/sqlite>) | BSD-3-Clause |
|
||||
|
||||
Transitive dependencies — `go mod graph` / `go.sum`; all indirect packages in
|
||||
the tree are AGPL-3.0-compatible.
|
||||
|
||||
### E2e module (`test/e2e/go.mod`)
|
||||
|
||||
| Package | Version | Repository | License |
|
||||
|---|---|---|---|
|
||||
| `github.com/emersion/go-msgauth` | v0.6.8 | <https://github.com/emersion/go-msgauth> | BSD-2-Clause |
|
||||
|
||||
The test module is not part of the main `go build` graph and is not shipped in
|
||||
the image.
|
||||
|
||||
### Debian packages in the runtime image
|
||||
|
||||
Postfix, OpenDKIM, `supervisord`, `sasl2-bin`, `logrotate`, and others come
|
||||
from Debian bookworm repositories; licenses are in each package's `copyright`
|
||||
file on <https://packages.debian.org/bookworm/>.
|
||||
|
||||
---
|
||||
|
||||
## Building binaries and the image
|
||||
|
||||
### Local binaries
|
||||
|
||||
Requires Go 1.26+ and `CGO_ENABLED=0`.
|
||||
|
||||
```sh
|
||||
make vet # go vet ./...
|
||||
make test # go test ./...
|
||||
make build # bin/panel, bin/selfpost-backup (VERSION=dev by default)
|
||||
make build VERSION=1.0.0
|
||||
make build VERSION=1.0.1
|
||||
```
|
||||
|
||||
Or directly:
|
||||
|
||||
```sh
|
||||
go vet ./...
|
||||
go test ./...
|
||||
go build -trimpath -ldflags "-X github.com/mixeme/selfpost/internal/buildinfo.Version=dev" -o bin/panel ./cmd/panel
|
||||
```
|
||||
|
||||
**Env documentation regression:** `go test ./cmd/panel -run TestLoadConfig` —
|
||||
new `loadConfig` keys must appear in README env lists
|
||||
([cmd/panel/envdoc_test.go](../cmd/panel/envdoc_test.go)).
|
||||
The version is stamped into both binaries via `-ldflags` and **must match the
|
||||
Docker image tag** — restore checks backup version compatibility.
|
||||
|
||||
---
|
||||
### Docker image
|
||||
|
||||
## End-to-end tests
|
||||
|
||||
Hermetic container suite (implemented as `test/e2e/`, separate Go module):
|
||||
From the repository root:
|
||||
|
||||
```sh
|
||||
make e2e
|
||||
# equivalent: cd test/e2e && go test -v -timeout 20m ./...
|
||||
```
|
||||
|
||||
**Stack:** shipped [deploy/docker-compose.yml](../deploy/docker-compose.yml) +
|
||||
[test/e2e/compose.override.yml](../test/e2e/compose.override.yml) — same
|
||||
`cap_drop`/`cap_add`/`no-new-privileges` as production. Override uses high
|
||||
ports (`20465`/`20587`/`20080`), test hostname, self-signed TLS,
|
||||
`PANEL_COOKIE_SECURE=false`, isolated compose project. Mail is hermetic: CoreDNS
|
||||
fake zone + Postfix `smtp-sink` as sink-MX; DKIM TXT is scraped from the panel
|
||||
and published into the zone so the test verifies the record the operator would
|
||||
use.
|
||||
|
||||
**Coverage (summary):** full bootstrap → SMTP AUTH → delivery → DKIM verify →
|
||||
send-log `queued → sent`; negatives (no AUTH, relay, sender/login mismatch, L1/L2
|
||||
limits, milter fail-open, bad `SELFPOST_HOSTNAME`, session survives
|
||||
`docker restart`). Polling with timeouts only — no fixed `sleep`.
|
||||
|
||||
Requires **Docker + Compose v2** on the test host. Not included in `go test ./...`
|
||||
of the main module.
|
||||
|
||||
**CI** ([release.yml](../.github/workflows/release.yml)): tag `vX.Y.Z` triggers
|
||||
`prepare` (version from tag) → native matrix `[ubuntu-latest, ubuntu-24.04-arm]`
|
||||
— build `--load`, e2e, push per-arch tag → `merge` publishes unified
|
||||
`ghcr.io/...:X.Y.Z` via `docker buildx imagetools create`. Failed e2e blocks the
|
||||
image. Ordinary pushes still run only `vet`/`test` in [test.yml](../.github/workflows/test.yml).
|
||||
|
||||
---
|
||||
|
||||
## Dev server (full container)
|
||||
|
||||
When unit tests are not enough — Postfix, OpenDKIM, supervisord, real SMTP:
|
||||
|
||||
**Typical setup:** edit locally → sync tree to dev host → build/test there.
|
||||
|
||||
Documented dev host: `selfpost.example.com` (Debian 12). Sync example from
|
||||
[progress.md](progress.md):
|
||||
|
||||
```sh
|
||||
tar -czf - --exclude=.git . | ssh root@selfpost.example.com \
|
||||
'rm -rf /root/selfpost-src && mkdir -p /root/selfpost-src && tar -xzf - -C /root/selfpost-src'
|
||||
```
|
||||
|
||||
On the server (Go in `/usr/local/go/bin` if not in PATH):
|
||||
|
||||
```sh
|
||||
cd /root/selfpost-src
|
||||
/usr/local/go/bin/go vet ./...
|
||||
/usr/local/go/bin/go test ./...
|
||||
docker build -f build/Dockerfile -t selfpost:dev --build-arg VERSION=dev .
|
||||
```
|
||||
|
||||
Manual smoke on production-like host: panel at `https://selfpost.example.com`,
|
||||
real LE cert, live deliverability — not replaceable by e2e alone (no outbound
|
||||
25 on CI runners, no real PTR/reputation).
|
||||
The Dockerfile has a build stage (`go vet`, `go build` with `VERSION`) and a
|
||||
runtime stage (Debian + mail stack). See [architecture.md](architecture.md) §
|
||||
Image and processes.
|
||||
|
||||
---
|
||||
|
||||
## Commit and changelog protocol
|
||||
## Commits and release build
|
||||
|
||||
From [progress.md](progress.md):
|
||||
Commit on every meaningful step (a working sub-feature, a green build, end of a
|
||||
phase) — not every file save, and not only at phase end. Minimum: one commit per
|
||||
closed phase, plus intermediate commits for coherent sub-steps. Branch `main`
|
||||
unless a separate branch is requested. Push / PR only on explicit request.
|
||||
|
||||
1. Meaningful step → entry under `[Unreleased]` in [CHANGELOG.md](../CHANGELOG.md)
|
||||
(Keep a Changelog format).
|
||||
2. Commit on `main` unless asked for a branch. **Do not commit unless the user
|
||||
asks.**
|
||||
3. Before `/clear` at end of a phase: update `progress.md`, verify acceptance
|
||||
criteria, CHANGELOG, final commit.
|
||||
Commit messages end with
|
||||
`Co-Authored-By: Claude <model> <noreply@anthropic.com>` for the model that did
|
||||
the step (e.g. `Claude Sonnet 4.6`).
|
||||
|
||||
Documentation changes that add/rename env vars, panel routes, or observable
|
||||
mail behaviour ship in the **same commit** as the code change.
|
||||
Every such step also updates [CHANGELOG.md](../CHANGELOG.md) under
|
||||
`[Unreleased]` (Keep a Changelog). On an explicit version cut, rename
|
||||
`[Unreleased]` to `[X.Y.Z] - date` and open a fresh empty `[Unreleased]`. Image
|
||||
tag / push only on explicit request (see `release.yml`).
|
||||
|
||||
Release tagging and image push — only on explicit request.
|
||||
### Release image
|
||||
|
||||
The release image is published **only on tag** `vX.Y.Z` (not on every push to
|
||||
`main`). The tag is the single source of version: it drives the image tag and
|
||||
`-ldflags` in the binaries so they cannot drift apart.
|
||||
|
||||
**Steps (on explicit request):**
|
||||
|
||||
1. Close `[Unreleased]` in [CHANGELOG.md](../CHANGELOG.md) and bump the pinned
|
||||
tag in [deploy/docker-compose.yml](../deploy/docker-compose.yml) (and any
|
||||
local-trial image references) in the **same** release commit.
|
||||
2. Create and push git tag `vX.Y.Z` on that commit.
|
||||
3. Workflow [release.yml](../.github/workflows/release.yml) builds, e2e-gates,
|
||||
and publishes `ghcr.io/mixeme/selfpost:X.Y.Z`.
|
||||
|
||||
Ordinary commits **do not** publish an image. The compose pin and the git tag
|
||||
must match (`1.0.0` / `v1.0.0` for the first published release). Intermediate
|
||||
CHANGELOG sections (`0.2.0`…`0.6.0`) record development history before that cut.
|
||||
|
||||
---
|
||||
|
||||
## Agent rules (formerly spec §12)
|
||||
## Phase closure
|
||||
|
||||
1. **No git commits** without explicit instruction in the prompt.
|
||||
2. After Go changes: `go build`, `go vet`; fix all issues. Run `go test` when
|
||||
tests exist.
|
||||
3. Before calling a container task done: image builds and container starts.
|
||||
4. Iterate: minimal skeleton first, then features.
|
||||
5. Security requirements in [security.md](security.md) — implement with the feature,
|
||||
not deferred.
|
||||
6. Do not implement out-of-scope items ([product.md](product.md)) or change
|
||||
fixed assumptions without agreement.
|
||||
7. For large tasks: propose a plan before coding unless the user already approved
|
||||
one.
|
||||
8. Check licence compatibility of new Go dependencies (permissive or
|
||||
GPL-family for AGPL-3.0 project).
|
||||
Before `/clear` at the end of a finished step:
|
||||
|
||||
Model routing (from progress): security/infra → Opus; UI/docs → Sonnet;
|
||||
trivial mechanics → Haiku. Pre-release security **review** (not authorship) →
|
||||
Fable.
|
||||
1. Update [roadmap.md](roadmap.md) (and the active plan checklist, if any): what
|
||||
changed, what is next.
|
||||
2. Check the applicable «Done when…» criteria.
|
||||
3. Append [CHANGELOG.md](../CHANGELOG.md) under `[Unreleased]`.
|
||||
4. Make the final commit for the step (when the user asks for a commit).
|
||||
|
||||
---
|
||||
|
||||
## Testing
|
||||
|
||||
### Static analysis and unit tests
|
||||
|
||||
Main module (`go test ./...`); e2e is a separate module — see below.
|
||||
|
||||
```sh
|
||||
make vet # go vet ./...
|
||||
make test # go test ./...
|
||||
```
|
||||
|
||||
Or directly:
|
||||
|
||||
```sh
|
||||
gofmt -l . # in CI — fails on drift
|
||||
go vet ./...
|
||||
go test ./...
|
||||
```
|
||||
|
||||
### Env documentation regression
|
||||
|
||||
`go test ./cmd/panel -run 'TestLoadConfigKeysDocumented|TestBuildScriptKeysDocumented|TestDocumentedKeysAreRead'`
|
||||
— every new `loadConfig` key must appear in the env lists in [guide.md](guide.md)
|
||||
([cmd/panel/envdoc_test.go](../cmd/panel/envdoc_test.go)).
|
||||
|
||||
### End-to-end (container suite)
|
||||
|
||||
Separate Go module `test/e2e/`; **not** included in the main module's
|
||||
`go test ./...`.
|
||||
|
||||
```sh
|
||||
make e2e
|
||||
# same as: cd test/e2e && go test -v -timeout 20m ./...
|
||||
```
|
||||
|
||||
**Stack:** [deploy/docker-compose.yml](../deploy/docker-compose.yml) +
|
||||
[test/e2e/compose.override.yml](../test/e2e/compose.override.yml) — same
|
||||
`cap_drop`/`cap_add`/`no-new-privileges` as production. Override: high ports
|
||||
(`20465`/`20587`/`20080`), test hostname, self-signed TLS,
|
||||
`PANEL_COOKIE_SECURE=false`, isolated compose project. Mail is hermetic:
|
||||
CoreDNS (fake zone) + Postfix `smtp-sink` as sink-MX; DKIM TXT is scraped from
|
||||
the panel and published into the zone — the test verifies the records an
|
||||
operator would actually use.
|
||||
|
||||
**Coverage (summary):** bootstrap → SMTP AUTH → delivery → DKIM verify →
|
||||
send-log `queued → sent`; negatives (no AUTH, relay, sender/login mismatch,
|
||||
L1/L2 limits, milter fail-open, bad `SELFPOST_HOSTNAME`, session survives
|
||||
`docker restart`). Polling with timeouts only — no fixed `sleep`.
|
||||
|
||||
Requires **Docker + Compose v2** on the machine running the suite.
|
||||
|
||||
---
|
||||
|
||||
## CI
|
||||
|
||||
Workflows in [.github/workflows/](../.github/workflows/). What each job runs —
|
||||
[§ Testing](#testing) above.
|
||||
|
||||
### `test.yml` — every push and PR to `main`
|
||||
|
||||
`gofmt -l` → `go vet ./...` → `go test ./...` (main module, no e2e).
|
||||
|
||||
### `release.yml` — push of tag `vX.Y.Z` or `workflow_dispatch`
|
||||
|
||||
```
|
||||
prepare (version from tag)
|
||||
→ build [matrix: ubuntu-latest / ubuntu-24.04-arm]
|
||||
→ docker build --load (VERSION from tag)
|
||||
→ e2e (test/e2e)
|
||||
→ push ghcr.io/...:X.Y.Z-amd64 | X.Y.Z-arm64
|
||||
→ merge
|
||||
→ docker buildx imagetools create → unified manifest X.Y.Z
|
||||
```
|
||||
|
||||
Native per-arch matrix (no QEMU): running the full Postfix/OpenDKIM stack under
|
||||
emulation for e2e is impractical. E2e first, then push — the registry receives
|
||||
the bytes that passed the gate.
|
||||
|
||||
A failed e2e **blocks** image publication.
|
||||
|
||||
---
|
||||
|
||||
## Documentation
|
||||
|
||||
History of deleted plans lives in git and [CHANGELOG.md](../CHANGELOG.md).
|
||||
There is no `docs/archive/` directory.
|
||||
|
||||
### Documentation map
|
||||
|
||||
| Home | File |
|
||||
|---|---|
|
||||
| Operator install / quick start | [README.md](../README.md) |
|
||||
| Operator guide | [guide.md](guide.md) |
|
||||
| Product boundaries | [product.md](product.md) |
|
||||
| As-built design | [architecture.md](architecture.md) |
|
||||
| Development process (this file) | [development.md](development.md) |
|
||||
| Security requirements and accepted risks | [security.md](security.md) |
|
||||
| Roadmap (1.x+) | [roadmap.md](roadmap.md) |
|
||||
| Active design plans | [plans/](plans/) |
|
||||
| Release history | [CHANGELOG.md](../CHANGELOG.md) |
|
||||
|
||||
### User-facing deliverables
|
||||
|
||||
| Artefact | Role |
|
||||
|---|---|
|
||||
| [README.md](../README.md) | Overview, requirements, quick start, docs index, reference deploy, licence |
|
||||
| [guide.md](guide.md) | Proxy, env, DNS, IP warmup, operations, rate limiting, backup, ports, image tag |
|
||||
| [SECURITY.md](../SECURITY.md) | Private reporting channel, supported versions, scope |
|
||||
| [LICENSE](../LICENSE) | AGPL-3.0 full text |
|
||||
| [deploy/docker-compose.yml](../deploy/docker-compose.yml) + proxies | Apache + nginx/Caddy/Traefik under [deploy/](../deploy/) |
|
||||
| [deploy/.env.example](../deploy/.env.example) | Public env template; full reference in [guide.md](guide.md) |
|
||||
| [CHANGELOG.md](../CHANGELOG.md) | Keep a Changelog |
|
||||
|
||||
Out of scope for v1.x: `CONTRIBUTING.md`, man pages, a separate docs site
|
||||
(candidates in [roadmap.md](roadmap.md)).
|
||||
|
||||
### Maintaining documentation
|
||||
|
||||
1. **Step rule:** a new or renamed env key, panel route, or observable mail-path
|
||||
behaviour ships together with [guide.md](guide.md) / `.env.example` and a
|
||||
CHANGELOG entry (see [§ Commits and release build](#commits-and-release-build)).
|
||||
2. **Env regression:** [cmd/panel/envdoc_test.go](../cmd/panel/envdoc_test.go)
|
||||
fails on an undocumented `loadConfig` or build-script key.
|
||||
3. **New gaps** go into [roadmap.md](roadmap.md) (or the active plan file), not
|
||||
silent drive-by edits.
|
||||
|
||||
### Verifying docs against code
|
||||
|
||||
Every claim in the docs has a **source of truth in the tree**; verify from code
|
||||
to prose.
|
||||
|
||||
| Claim class | Source of truth |
|
||||
|---|---|
|
||||
| Env keys and defaults | `loadConfig` — [cmd/panel/main.go](../cmd/panel/main.go); `${VAR:-…}` in [build/](../build/) |
|
||||
| Mail path | [build/postfix-config.sh](../build/postfix-config.sh) |
|
||||
| Panel routes | [internal/web/web.go](../internal/web/web.go) |
|
||||
| Backup / restore, domain export | [internal/backup/](../internal/backup/), [cmd/selfpost-backup/](../cmd/selfpost-backup/) |
|
||||
| Sessions | [internal/store/sessions.go](../internal/store/sessions.go), [internal/web/session.go](../internal/web/session.go) |
|
||||
| Log rotation, reload | [build/logrotate-mail.conf](../build/logrotate-mail.conf), [build/logrotate-loop.sh](../build/logrotate-loop.sh), [build/postfix-cert-reload.sh](../build/postfix-cert-reload.sh) |
|
||||
| Deploy | [deploy/docker-compose.yml](../deploy/docker-compose.yml), [build/Dockerfile](../build/Dockerfile) |
|
||||
| Operator checklist | [§ User-facing deliverables](#user-facing-deliverables); detail — [guide.md](guide.md) |
|
||||
| Product / out of scope | [product.md](product.md) |
|
||||
| As-built | [architecture.md](architecture.md) |
|
||||
| Mandatory security | [security.md](security.md) |
|
||||
|
||||
Order: list what the code actually does → find it in [guide.md](guide.md) /
|
||||
`architecture.md`. Before every tag, a short pass over this table — not a full
|
||||
prose rewrite.
|
||||
|
||||
Reference in New Issue
Block a user