My first repository on GitHub! https://hello-world-imp-e2e-org.open-platform.sh
  • TypeScript 80.4%
  • CSS 15.1%
  • Go Template 1.9%
  • Dockerfile 1.8%
  • HTML 0.8%
Find a file
plat 3528a8bb19
All checks were successful
sync-env / sync-env (push) Successful in 1s
check / check (push) Successful in 11s
release / build (push) Successful in 16s
platform: hello-world — the greetings wall (#2)
2026-08-15 18:21:18 +00:00
.forgejo/workflows platform: hello-world — the greetings wall (#2) 2026-08-15 18:21:18 +00:00
charts/app platform: hello-world — the greetings wall (#2) 2026-08-15 18:21:18 +00:00
design platform: hello-world — the greetings wall (#2) 2026-08-15 18:21:18 +00:00
src platform: hello-world — the greetings wall (#2) 2026-08-15 18:21:18 +00:00
web platform: hello-world — the greetings wall (#2) 2026-08-15 18:21:18 +00:00
wiki platform: hello-world — the greetings wall (#2) 2026-08-15 18:21:18 +00:00
.gitignore platform: hello-world — the greetings wall (#2) 2026-08-15 18:21:18 +00:00
biome.json platform: hello-world — the greetings wall (#2) 2026-08-15 18:21:18 +00:00
bun.lock platform: hello-world — the greetings wall (#2) 2026-08-15 18:21:18 +00:00
Dockerfile platform: hello-world — the greetings wall (#2) 2026-08-15 18:21:18 +00:00
MIGRATION.md platform: hello-world — the greetings wall (#2) 2026-08-15 18:21:18 +00:00
mobile-top.png platform: hello-world — the greetings wall (#2) 2026-08-15 18:21:18 +00:00
package.json platform: hello-world — the greetings wall (#2) 2026-08-15 18:21:18 +00:00
README New line at end of file. --Signed off by Spaceghost 2011-09-13 21:42:41 -07:00
README.md platform: hello-world — the greetings wall (#2) 2026-08-15 18:21:18 +00:00
tsconfig.json platform: hello-world — the greetings wall (#2) 2026-08-15 18:21:18 +00:00
vite.config.ts platform: hello-world — the greetings wall (#2) 2026-08-15 18:21:18 +00:00

hello-world

Hello World!

That one line is the entire history of this repo — it was migrated from octocat/Hello-World, GitHub's original demo repository, and the original README is still here, untouched.

This app gives that line some company: a wall of hellos. Twelve greetings ship with it; anyone signed in can add theirs and remove their own.

Live: https://hello-world-imp-e2e-org.open-platform.sh

What it does

  • The wall — every greeting, newest first, as cards showing the greeting, its language, who posted it, and when.
  • Filter — type in the search box to narrow the wall by language, text or author. Instant, client-side; GET /api/greetings?q= does the same thing for API consumers.
  • Post a greeting — the composer takes a language and a greeting. Where the deployment has a sign-in, posting requires it and the greeting is attributed to your account; where it has none, the wall takes greetings from anyone, with an optional "From" signature (see Auth).
  • Remove your own — the Remove button only appears on greetings you posted, and the API enforces that independently (403 otherwise).

Stack

Layer Choice
Server Fastify 5 + TypeScript (src/), TypeBox schemas → generated OpenAPI
UI React 19 SPA (web/), component kit in web/src/ui/
Data Postgres (CNPG), migrations in src/migrate.ts
Styling Org design tokens only, served at /tokens.css
Lint biome
Runtime Bun on oven/bun:1-alpine (see Dockerfile)

Endpoints

Method Path Auth What
GET / public The SPA
GET /version public { name, version, store }
GET /readyz public Readiness — is the store answering?
GET /api/me public Who is asking (anonymous → null)
GET /api/greetings public The wall (?q= filters)
POST /api/greetings see below Add a greeting
DELETE /api/greetings/:id session Remove your own greeting
GET /openapi.json public OpenAPI 3.1, generated from the routes
GET /tokens.css public Design token fallback stylesheet

Auth

The app never assumes an edge is gating it: it resolves identity itself, on every request, in a preValidation hook that runs before a body is parsed.

A wall of hellos holds nothing private, so reads are public everywhere. Writes follow the identity the deployment actually has:

Deployment Posting Removing
In-app sign-in exists (mode: app) Requires a session; attributed to your account Your own greetings
Behind the forwardAuth edge (mode: platform) Every request is already authenticated → attributed to it Your own greetings
No identity source (mode: none) Open, signed with an optional "From" name, rate-limited per IP Nobody

That last row is a deliberate choice: this deployment has no identity source (the platform's forwardAuth edge is off org-wide and no in-app OAuth credentials are provisioned), and a wall nobody can ever write to is an ornament. Nothing on the wall is private, so opening posting there costs no confidentiality — and the moment a sign-in exists, the app requires it again with no code change. Deleting is always identity-bound and checked server-side.

Identity comes from whichever source the deployment configures (src/config.ts, authMode()):

  • platform (PLAT_FORWARD_AUTH=1) — the platform edge authenticates the request and injects verified X-Plat-* headers. When PLAT_EDGE_TOKEN is configured, the app additionally demands that shared secret before trusting any header, so hand-crafted headers sent straight at the pod fail closed.
  • app (FORGEJO_BASE + FORGEJO_CLIENT_ID + FORGEJO_CLIENT_SECRET) — in-app Forgejo SSO, OAuth2 authorization-code flow, session in an HMAC-signed httpOnly cookie (BETTER_AUTH_SECRET signs it).
  • none — no identity source configured: everyone is anonymous, so the wall accepts signed-but-unauthenticated greetings (10 per IP per 10 minutes) and the UI says so instead of offering a sign-in that cannot work.

Data

One table, greetings, created and seeded by numbered migrations in src/migrate.ts (see wiki/Data-Model.md). Migrations run at boot, before the pod reports ready, and are also runnable standalone (npm run migrate / bun dist/migrate.js). To change the schema, append a migration — never edit an applied one.

When no database is configured (local dev, or a deployment provisioned without one) the app falls back to a JSON file on its mounted volume rather than refusing to boot, and /readyz reports which store answered — degraded: true if Postgres was configured but unreachable.

Development

bun install
npm run build      # tsc → dist/, vite → web/dist/
npm start          # serve on :8080 (DATA_DIR=/tmp for the file store)
npm run lint       # biome
npm run typecheck  # server + web

npm run lint, npm run typecheck and npm run build all have to pass before anything is pushed; CI (.forgejo/workflows/check.yml) additionally builds the container image from the Dockerfile.

Design tokens

The UI is styled only with the org's design vocabulary — var(--color-…), var(--space-…), var(--radius-…) and friends. design/tokens.default.json is this app's fallback copy of that vocabulary, compiled to /tokens.css at boot; when the org runs a design app, PLAT_DESIGN_URL makes its tokens.css load afterwards so org values win and a dead design app degrades to the defaults instead of to an unstyled page.

Platform plumbing

Dockerfile, charts/app/ and .forgejo/workflows/ come from the platform and are left alone. The chart mounts a volume at /data, probes /readyz for startup and readiness and /version for liveness, and injects PORT, APP_NAME, PLAT_* and the per-app database/auth secrets.