Skip to content

Refactor(server): move the Go server to ZenNotes/znserver - #795

Merged
adibhanna merged 2 commits into
mainfrom
refactor/server-cutover
Sep 16, 2026
Merged

adibhanna merged 2 commits into
mainfrom
refactor/server-cutover

Conversation

@adibhanna

Copy link
Copy Markdown
Contributor

Stacked on #789 (refactor/ecosystem-boundaries-pr). Draft until the boundary PR lands.

What this does

The Go server now lives in ZenNotes/znserver (history extracted from apps/server, release v2.50.4 published, Docker image adibhanna/zennotes:2.50.5 built there). This PR removes the copy that stayed in the monorepo and redirects everything that still built the server from source at the published release.

  • Removes apps/server, the root Dockerfile/.dockerignore, docker-publish.yml, the server Nix package and vendorHash, tooling/server-repository, and the server build/staging/rehearsal scripts.
  • Adds tooling/server-release.json (pinned tag plus per-platform SHA-256) and tooling/scripts/server-binary.mjs, which resolves a server binary in a fixed order: ZENNOTES_SERVER_BINARY, a ZENNOTES_SERVER_DIR checkout built with Go, or the pinned release downloaded and checksum-verified before use.
  • dev:server, dev:web-stack, and test:app-core-browser use the resolver. Browser work needs neither Go nor a server checkout.
  • perf:web-runtime requires a znserver checkout or an explicit embedded-web binary, and says so; it never silently measures the pinned artifact.
  • Compose runs the published image (IMAGE= to pin); make rebuild pulls, make nuke removes the pulled image.
  • CI drops the Go setup, the server job, and the Go fixture-copy check (the server's own CI checks its copies). Docs and synced-copy comments point at the new repository.

Verified locally

  • npm run typecheck, npm run test:run (app-core 2356 tests, desktop 792), npm run build, test:web-dist-lock, test:web-artifact, test:app-core-package, test:app-core-browser (against the downloaded pinned binary).
  • npm run server:binary downloads zennotes-server-darwin-arm64 from the v2.50.4 release and verifies the pinned digest; a second call reuses the cached file.
  • npm run dev:server with an isolated vault, token, and port: /api/healthz 200, /api/notes 401 without the token, note write and read round trip, embedded UI served.
  • docker compose with the repo's Compose file and adibhanna/zennotes:2.50.5: healthz 200, 401 without token, note write lands in the mounted vault; image labels show source ZenNotes/znserver.
  • perf:web-runtime end to end with ZENNOTES_SERVER_DIR pointing at a znserver checkout (local web dist synced in, embedded server built, CDP run).
  • Contract fixtures compared against the znserver checkout: match.

Not in this PR

  • Moving the Docker latest tag (happens with the next server release from znserver).
  • The CSP fix for the inline service-worker registration in apps/web/index.html, which the server's CSP blocks (pre-existing, tracked separately).

The self-hosted Go server now lives in its own repository,
https://github.com/ZenNotes/znserver. Its history was extracted from
apps/server with the full commit trail, its first release (v2.50.4) is
published with checksummed binaries, and the adibhanna/zennotes Docker
image is built and pushed by that repository's publisher (2.50.5 was the
first image from the new channel). This commit removes the copy that
stayed here and points everything that still built the server from
source at the published release instead.

What goes: apps/server, the root Dockerfile and .dockerignore, the
docker-publish workflow, the server Nix package and its vendorHash, the
server-repository templates, the extraction rehearsal script, and the
scripts that built or staged the server (build-go-server,
prepare-server-web-dist, run-go-server-test). CI no longer installs Go,
runs the server job, or checks the Go fixture copies, because those
copies are checked in the server's own CI.

What replaces it: tooling/server-release.json pins the znserver release
(tag plus per-platform SHA-256 from its SHA256SUMS), and
tooling/scripts/server-binary.mjs resolves a server binary in a fixed
order: an explicit ZENNOTES_SERVER_BINARY, a ZENNOTES_SERVER_DIR checkout
built with Go, or the pinned release downloaded into dist/server-binaries
and verified against the pinned digest before it is ever executed. A
download whose checksum does not match is refused rather than run.
dev:server, dev:web-stack, and the app-core browser harness use that
resolver, so browser work needs neither Go nor a server checkout.

The perf harness deliberately does not fall back to the pinned release:
a perf run measures the local web bundle, and the release embeds the
pinned artifact instead. It takes a znserver checkout (syncing the local
web dist in, then building with -tags=embed_web) or an explicit binary,
and fails with the two options spelled out otherwise. sync-web-dist and
the contract-fixture sync take the checkout as their destination for the
same reason; the web-dist lock now lives next to apps/web/dist, the tree
it guards.

Docker Compose runs the published image (adibhanna/zennotes:latest by
default, IMAGE=... to pin) instead of building one; make rebuild pulls,
make nuke removes the pulled image. README, guide, CONTRIBUTING, the
packaging docs, the architecture notes, and the synced-copy comments in
the TypeScript sources point at the new repository. The extraction
rehearsal and cutover documents keep their content with a status banner
recording that the move is done.

Not changed: the server's own release process (bump release.json in
znserver, run its release workflow, then its Docker publisher), the web
artifact pipeline that znserver consumes, and the moving of the Docker
`latest` tag, which happens with the next server release.
The web artifact workflow still ran check:contract-fixtures. That script
now compares the shared fixtures against a ZenNotes/znserver checkout, and
the runner has none, so the step failed before the artifact was built.
The Go copies are checked by the server's own CI against the same bytes,
and the TypeScript consumers keep verifying the fixtures through test:run,
so the step goes rather than gaining a checkout it does not need.
@adibhanna
adibhanna marked this pull request as ready for review September 16, 2026 20:07
Base automatically changed from refactor/ecosystem-boundaries-pr to main September 16, 2026 20:40
@adibhanna
adibhanna merged commit 8caedda into main Sep 16, 2026
8 checks passed
@adibhanna
adibhanna deleted the refactor/server-cutover branch September 16, 2026 20:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant