Infrastructure
Security
Security boundaries, implemented controls, known gaps, and planned hardening.
ByteBite staging follows a defense-in-depth model: minimal public exposure, internal-only services, and strict tenant trust boundaries.
| Control | Detail |
|---|
| Public ports | Only 80 and 443 on 88.99.0.118 (verified 2026-09-08) |
| App ports closed | 3000, 3001, 3002, 5432, 8080 not reachable externally |
| Internal routing | Traefik → Docker network only |
| PostgreSQL | No public port; internal Dokploy network only |
| Control | Detail |
|---|
| HTTPS everywhere (staging) | HTTP → HTTPS redirect verified on all explicit hosts |
| Valid certificates | Let's Encrypt HTTP-01 (explicit) + DNS-01 (wildcard) |
| Separate ACME stores | acme.json and acme-cloudflare.json isolated |
| Control | Detail |
|---|
trustProxy: false | Fastify does not trust X-Forwarded-* from clients |
Tenant from Host only (external) | Browser and same-origin /api use Traefik-delivered Host |
X-ByteBite-Tenant-Host restricted | Honored only when request Host is internal (api, localhost, 127.0.0.1, ::1) |
| No public API port | Raw api:3001 not host-published |
| Exact hostname lookup | No slug inference from DNS labels |
| Control | Detail |
|---|
| No secrets in Git | .env git-ignored; Dokploy env for staging |
| Cloudflare token scoped | Zone Read + DNS Edit on getbytebite.co only |
| Token file permissions | 600, root:root on host filesystem |
| ACME keys on host | Not in repository |
| Control | Detail |
|---|
| Non-root runtime | Node apps run as node user; nginx unprivileged for dashboard |
| Docker socket | Traefik mounts read-only |
| No privileged mode | Not used in deployment compose |
| No bind mounts (deploy) | Application images are self-contained |
| Control | Detail |
|---|
| DNS-only Cloudflare | Not proxied — Traefik terminates TLS directly |
| Edge indirection | edge.getbytebite.co centralizes IP changes |
| Control | Detail |
|---|
noindex, nofollow | Storefront, docs (when BYTEBITE_DEPLOY_ENV=staging), dashboard (always) |
| Gap | Severity | Status |
|---|
Dashboard SPA fallback returns HTTP 200 for /.env, /.git/HEAD, etc. | Medium | OPEN |
| No staging access protection (Cloudflare Access / BasicAuth) | Low (staging) | OPEN |
| Unknown storefront tenant returns HTTP 200 instead of 404 | Low (SEO) | OPEN — application issue |
| No WAF or rate limiting at edge | Medium | OPEN |
| No centralized security monitoring | Medium | OPEN |
| Public internet scanner noise | Informational | ACCEPTED — normal for exposed services |
| Dokploy Traefik custom wiring may not survive upgrades | High | OPEN — ops risk |
Requests such as /.env, /.git/HEAD, /.git/config to the dashboard return HTTP 200 with the SPA index.html (~566 bytes). This is not evidence of real file exposure but is undesirable HTTP semantics. Storefront and docs correctly return 404 for these paths.
Public internet scanners probe common paths (.env, .git, GraphQL, PHP, WordPress). This is normal for internet-exposed services and does not indicate compromise.
| Item | Status |
|---|
| Staging access protection (Cloudflare Access or Traefik BasicAuth) | PLANNED — not enabled |
| Webhook exceptions if access protection added | PLANNED |
| Production WAF strategy | PLANNED |
| Production custom-domain TLS verification | PLANNED |
| CSP and security headers audit | PLANNED |