- Dockerfile 28.6%
- Shell 20.8%
- CSS 20.1%
- HTML 19.8%
- TypeScript 10.7%
|
|
||
|---|---|---|
| .forgejo/workflows | ||
| app | ||
| db/init | ||
| deploy | ||
| images | ||
| nginx/conf.d | ||
| site | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| CODEOWNERS | ||
| docker-compose.yaml | ||
| Dockerfile | ||
| README.md | ||
CPI — Coming Soon
Production-ready static landing page (logo + "Coming Soon") served by a hardened, non-root nginx container. Built to be the seed of the full site: the same compose file already defines the future Node/TypeScript app and PostgreSQL services (off by default) so features like contact forms, maps, auth, and a database drop in without rework.
TLS is terminated upstream by Nginx Proxy Manager (10.15.20.115), which
proxies the public domain to this host's :8080. This container speaks plain
HTTP on the internal network only.
Layout
Dockerfile # nginx-unprivileged static site
nginx/conf.d/default.conf # server block + security headers
site/ # index.html, styles.css, assets/
app/ # future Node/TS (Hono) stub
db/init/ # future Postgres init scripts
docker-compose.yaml # site now; app+db via --profile future
.forgejo/workflows/ # CI: build -> Trivy -> push
.env.example # template (copy to .env, never commit)
Local bring-up
cp .env.example .env # fill values (FORGEJO_TOKEN already supplied)
docker compose up -d # runs only the nginx site on :8080
curl -I http://localhost:8080
Point Nginx Proxy Manager at 10.15.20.115 → this host :8080, enable SSL.
Future services (scaffolded, off by default)
docker compose --profile future up -d # adds cpi-app (3000) + cpi-db (5432, internal)
cpi-app is a Hono stub: GET /health → 200, any /api/* → 501. Build it out
for contact forms, maps, and auth. cpi-db is Postgres 17, reachable only on the
internal compose network (not published).
CI/CD (Forgejo)
.forgejo/workflows/build.yaml runs on push to main:
- Build the site image with Buildx.
- Push to
forge.sabylasolutions.com/sabyla/cpi. - Trivy scans for CRITICAL/HIGH CVEs (fails the build if found).
Required Forgejo repository secrets
The .env on this host is for local use and is gitignored. CI cannot read it,
so add these as repository secrets in Forgejo. Secret names may contain only
letters, numbers, and _, must not start with FORGEJO_, GITEA_, GITHUB_,
or a digit, and are auto-uppercased:
REGISTRY_USER— registry user (e.g.sabyla)REGISTRY_TOKEN— application token (the one already in.env; mirror here)
(The local .env keys FORGEJO_USER/FORGEJO_TOKEN are just local env vars and
are unaffected by this naming rule.)
Security baseline
- Non-root everywhere:
nginxinc/nginx-unprivileged(port 8080); app runs as uid 1001 withtinifor signal handling. - Hardened runtime:
read_only: true,tmpfs: /tmp,cap_drop: [ALL],no-new-privileges: true. - HTTP security headers: strict CSP (
default-src 'self'),nosniff,X-Frame-Options: DENY,Referrer-Policy: no-referrer,Permissions-Policy, andStrict-Transport-Security(enforced end-to-end via the upstream proxy). - No secrets in images:
.dockerignoreexcludes.env; CI uses secret mounts. - Firewall: allow only SSH +
10.15.20.115→:8080; drop everything else. - Supply chain: CVE gate in CI.
- CODEOWNERS gates changes to compose, nginx config, app, and CI.
Compliance notes (HIPAA + PCI — future)
- PCI: never store card data. Use a tokenizing processor (Stripe) so PANs never reach this stack; keep the DB out of PCI scope.
- HIPAA: PHI must be encrypted at rest (LUKS on host / encrypted volume) and
in transit (TLS at the proxy), access-logged, and hosted on a BAA-eligible
provider. Keep
cpi-dbon the internal network only and back it up. - Rotate service secrets on team changes; patch CRITICAL/HIGH CVEs within 24h.
Untracked files
.env, AI/assistant files, and local planning/dev scratch are gitignored and
never committed. Only the deployable artifacts above are tracked.