Content Management that can withstand the plundering hoards

Run the CMS and frontend locally

This setup runs Go binaries and the built browser editor on loopback addresses, with temporary private/published directories. It requires the build prerequisites and a development OAuth2/OIDC client. It does not start Kubernetes or an identity provider. For UI testing without manually configuring a provider, use the browser test harness.

Prepare a development sign-in client

At your development IdP, register callback http://127.0.0.1:18080/oauth2/callback and the scopes/roles you need. The IdP must permit this loopback callback. Use an HTTPS issuer reachable from the local CMS. If your provider requires HTTPS callbacks even locally, run behind a trusted local TLS proxy, register its hostname, and retain secure cookies instead.

In a terminal at repository root:

viking_dev_dir=$(mktemp -d)
mkdir -p "$viking_dev_dir/auth" "$viking_dev_dir/private" "$viking_dev_dir/published"
chmod 700 "$viking_dev_dir" "$viking_dev_dir/auth"
openssl rand -base64 32 > "$viking_dev_dir/auth/jwtSecret"
printf '%s' 'YOUR_DEV_CLIENT_ID' > "$viking_dev_dir/auth/clientID"
printf '%s' 'https://YOUR_HTTPS_ISSUER' > "$viking_dev_dir/auth/discoveryURL"
printf '%s' 'true' > "$viking_dev_dir/auth/publicClient"

Replace the client ID/issuer placeholders. This example uses a public PKCE client with no client secret; it must be registered as such. For a confidential client, remove/disable publicClient and write its actual clientSecret to a same-named file using your secure local secret workflow. Each auth key is a file, not one YAML document. Put development credentials outside the repository.

Keep this shell open: the following commands reuse viking_dev_dir. A loopback HTTP IdP is permitted only with --allow-insecure-auth-http=true; it remains forbidden for non-loopback hosts. Do not add this flag for an HTTPS provider.

Build and start the CMS

(cd cms/editor && npm ci && npm run build)
go run ./cms/cmd/cms \
  --listen=127.0.0.1:18080 \
  --admin-listen=127.0.0.1:18081 \
  --private-root="$viking_dev_dir/private" \
  --published-root="$viking_dev_dir/published" \
  --webui-dir="$PWD/cms/editor/dist" \
  --auth-secret-dir="$viking_dev_dir/auth" \
  --cms-hostname=127.0.0.1 \
  --cookie-secure=false --csrf-secure=false \
  --retain-releases=1 \
  --site-domain=http://127.0.0.1:18082

The process remains in the foreground. In a second terminal, curl -i http://127.0.0.1:18081/readyz should become 200; an unauthenticated curl -i http://127.0.0.1:18080/api/me should return 401. If auth is missing or invalid, expect 503, not an unauthenticated editor. The CMS cannot be used by omitting auth configuration. Restart it after changing local auth files.

Open http://127.0.0.1:18080 in a browser, sign in, create a homepage /, save and publish. All changes stay in the temporary directories you selected. Local status synchronization is disabled because no Kubernetes Website name/namespace is set. Plain builds identify themselves as Viking CMS dev.

Start a public frontend

In a third terminal at repository root, set viking_dev_dir to the same actual path printed by printf '%s\n' "$viking_dev_dir" in the first terminal. Then:

go run ./frontend/cmd/webserver \
  --root="$viking_dev_dir/published/current" \
  --listen=127.0.0.1:18082 \
  --admin-listen=127.0.0.1:18083

Visit http://127.0.0.1:18082/. Before the first publication there is no current release, so frontend readiness fails. Publish again and check the new content; with default freshness it can take about one second for the frontend to resolve the new pointer. Use the CMS to edit, not the public address.

Iterating on browser code

The reliable same-origin path is to rebuild cms/editor/dist and reload the CMS page after saving your work. The Go backend serves files from the specified directory, so a browser-only rebuild does not need a Go restart.

(cd cms/editor && npm run dev) runs Vite on 127.0.0.1:5173 with a strict port. The checked-in Vite configuration has no API/OAuth proxy; opening that origin alone does not produce a working authenticated CMS. A development proxy would need to preserve origin, cookie, callback, CSP and Host behaviour. Use the built bundle or browser harness unless you are deliberately implementing that setup. A plain python3 -m http.server also serves only files, not the CMS API/auth.

Stop and keep or remove data

Use Ctrl+C in the Go server terminals. Keep the temporary directory if you want to reuse drafts/releases, and reuse its auth signing key for compatible small sessions; in-memory login/CSRF/reference sessions still reset. Treat the folder as private because it includes credentials and source. Delete it only after saving anything needed elsewhere and stopping all processes that use it. Never use this loopback insecure-cookie setup for a publicly exposed deployment.