Перейти до вмісту

Pre-publication checklist for the public repository

How the public tree is produced: public-snapshot.md

The product is published as Tabula, in the public repository tabula-cms/tabula. This private repository, which carries the first installation’s history, is not switched to public: the public repository starts from a fresh snapshot of this tree (public-snapshot.md). Work through this list before the first public push; the PR that prepares the publication ticks what it completes. Items left open marked (owner) are settings only the repository owner can change.

  • Fresh history — one initial commit of the prepared tree, not git filter-repo over this repository. Decided 2026-09-27: it is the only way to be sure nothing old leaks, and it lets the public tree drop files an installation still relies on by name (migration 0013, see public-snapshot.md) without rewriting anything here.
  • Every check below is repeated against what will be pushed — the clone in section 8 — not only against this repository’s working tree.
  • Full history scanned with gitleaks (command below): 0 findings, 2026-09-27. --log-opts="--all" covered every branch and tag.
  • Nothing to rotate: no secret was ever committed (the scan above). Rotate first if a later scan ever finds one — removing a secret from history does not un-leak it.
  • .env is ignored (.gitignore) and was never committed (git log --all -- .env prints nothing).
  • GitHub Actions secrets and variables are not copied to the public repository — it deploys nothing. (owner)
Terminal window
gitleaks detect --source . --log-opts="--all" --redact --report-path gitleaks-report.json

3. Personal data and one school’s content

Section titled “3. Personal data and one school’s content”

Data about minors is treated as sensitive: names, photos, classes of students must not appear anywhere in the public repository or its history.

  • Historical content files (data/*.json, the PDFs under public/assets/docs/, the images under public/assets/images/ of the pre-database site) live only in this repository’s old commits; the fresh history never has them.
  • Test fixtures (test/): the first installation’s literals (name, address, map coordinates, album, archive prefix, SITE_SLUG) replaced with invented ones; sample names are obviously invented.
  • Docs: docs/audits/ and docs/design/ (the first installation’s design hand-off, reviews quoting its content) are excluded from the snapshot; docs/issues/**, docs/dev/, docs/editor/ and docs/install/ are scrubbed of its name, domain and city.
  • Installation-specific text in the tree — removed or neutralised: README.md (rewritten as Tabula’s README), CLAUDE.md (“What this is”), DEPLOYMENT.md (placeholders instead of the first installation’s values), migration 0013-seed-original-site-texts.sql (deleted — the runner ignores an applied file that is gone), the legacy import (src/db/importLegacy.js, src/db/registerFile.js, src/db/pagesSeed.js, scripts/import-legacy.js, npm run import:legacy), public/images/emblema.jpg, the school’s coordinates in src/lib/mapLinks.js and the tests. The only remaining mentions are in migrations 0006 and 0009, whose text is frozen by its checksum (editing them would stop every existing installation from starting in production); see public-snapshot.md. Verify with the command below (each word is split in two quoted halves the shell joins back, so that this page does not match itself); it prints only src/db/migrations/0006-menus.sql and src/db/migrations/0009-albums.sql. test/neutralTemplate.test.js holds the same list and fails if any other deployed file names the first installation.
  • Commit messages and author e-mails: the fresh history is one commit by the owner.
Terminal window
git grep -il -e 'koro''liov' -e 'Корол''ьов' -e 'Жито''мир' -e 'li''cey' -- ':!docs/audits' ':!docs/design'
  • Name: Tabula — package.json name, the Docker image title, /opt/tabula, ghcr.io/tabula-cms/tabula. Names an existing installation has on disk (SITE_SLUG, the session cookie, archive names, site.db) are untouched.
  • package.json author, description, repository, homepage, bugs set.
  • SECURITY.md points at GitHub private vulnerability reporting (/security/advisories/new) instead of an e-mail address.
  • The owner has chosen a licence: AGPL-3.0-only, 2026-09-26 (licensing.md).
  • package.json (and the root entry of package-lock.json) set to "license": "AGPL-3.0-only"; README «Ліцензія» section. No SPDX headers in source files — by decision.
  • LICENSE — pending diff against https://www.gnu.org/licenses/agpl-3.0.txt (whitespace included); being verified separately.
  • The font’s licence: public/fonts/OFL-Commissioner.txt (SIL OFL 1.1, the text published with Commissioner in google/fonts), mentioned in README and licensing.md.
  • Decide how a running site offers its source to its users (licensing.md → «Still open»).

6. Releases, images and update notifications

Section titled “6. Releases, images and update notifications”
  • The first release is tagged in the public repository (vX.Y.Z), so its Release and its ghcr.io/tabula-cms/tabula:X.Y.Z image exist there.
  • GHCR package visibility set to public (Package settings → Change visibility). Until then every other school needs a token to docker pull, and the update check gets 404. (owner)
  • Package linked to the public repository (the image’s org.opencontainers.image.source label comes from the build argument SOURCE_URL, i.e. github.repository; url is fixed to the project page).
  • UPDATE_REPO for installations that follow releases: tabula-cms/tabula (.env.example, docs/install/).
  • The first installation keeps deploying from this private repository, and its deploy writes UPDATE_REPO from github.repository — a private repository, so its dashboard stays silent. Decide whether it should announce the public releases instead.
  • The install guide (docs/install/) uses tabula-cms/tabula in every command and the raw.githubusercontent.com/tabula-cms/tabula/main/scripts/server-setup.sh URL. That the URL works for an anonymous user is checked in section 8.
  • Default branch chosen (develop or main) and documented in CONTRIBUTING.md.
  • Branch protection on the default branch: PR required, CI (ci.yml: jobs test, a11y, docker) required, no force-push, no deletion. (owner)
  • Tag protection for v* (only maintainers can create release tags). (owner)
  • Actions: workflows from forks cannot access secrets (GitHub default — verify). The deploy workflows are not in the public snapshot (public-snapshot.md), so there is nothing to disable.
  • Private vulnerability reporting enabled (Security → Private vulnerability reporting), as SECURITY.md promises. (owner)
  • Issue templates / labels as the project needs; Dependabot (.github/dependabot.yml) kept.
  • Clone the public repository into an empty directory as an anonymous user, follow docs/install/ on a throwaway server end to end, and bring up a site through the wizard. (owner)
  • Re-run gitleaks on that clone.
  • On that site, one pass with a screen reader — NVDA with Firefox or Chrome on Windows, and TalkBack on an Android phone: home, a text page through the menu, a post, the document library, an album with the lightbox, the contact form (send one message, hear the result), and the low-vision toggle. The automated check (npm run test:a11y, docs/dev/testing.md → «Accessibility») cannot tell whether the reading order and the announcements make sense.