Perspective V Docs

Finalized Requirements

Migrated from the repository documentation set.

Infrastructure Baseline

  • Migration source remains existing Plesk hosting.
  • Target host is a clean Contabo VPS on Ubuntu 24.04.
  • Docker and Docker Compose are installed and configured.
  • Shell baseline includes zsh.
  • Server capacity is 4 CPU cores, 8 GB RAM, 150 GB disk.
  • IP stack requirement is dual stack (IPv4 and IPv6).

Fresh VPS Reset Decisions (Current Priority)

  • Rebuild from scratch on the clean VPS.
  • Deploy dedicated edge Traefik from runtime/stacks/infrastructure/edge as the day-one reverse proxy baseline.
  • Deploy NetBird using official quickstart with embedded IdP quickstart.
  • Use NetBird Existing Traefik mode (script option 1) to integrate with the dedicated edge Traefik.
  • Keep NetBird proxy service disabled for now.
  • NetBird host remains netbird.perspective-v.com.
  • NetBird dashboard compose service must load runtime env from runtime/environments/vps/infrastructure/netbird/.env so AUTH/OIDC and management endpoint variables are present after image updates.
  • NetBird embedded IdP dashboard auth must keep AUTH_CLIENT_SECRET empty in VPS runtime env (runtime/environments/vps/infrastructure/netbird/.env) and tracked placeholder (runtime/environments/vps/infrastructure/netbird/netbird.env).

DNS and Exposure Policy

  • DNS provider is Namecheap.
  • Keep apex (@) on GitHub Pages.
  • Keep www and learn on GitHub Pages.
  • Use wildcard app routing to VPS for Docker-hosted app subdomains.
  • NetBird domain must have valid A and AAAA records to VPS.
  • Admin panel host routes may exist publicly, but access must be restricted to NetBird source ranges.
  • Traefik dashboard keeps Traefik edge basic-auth middleware; built-in-login panels use their native app authentication.

Security and Access Control

  • Public firewall exposure should be limited to 80/tcp, 443/tcp, and 3478/udp.
  • Databases and admin panels must be accessible only when connected to NetBird VPN.
  • Redis developer endpoint must be accessible only when connected to NetBird VPN.
  • Enforce access with interface-scoped UFW rules on NetBird interface (wt0 or detected equivalent).
  • Close broad public allows for 1433, 5432, 3306, 27017, and 6379 after NetBird validation.
  • Keep Fail2ban selected as host intrusion protection.
  • Keep 80/tcp and 443/tcp publicly reachable for internet-facing websites and APIs.
  • Incident response for outbound SSH spikes must include host-plus-container attribution (process, PID/container, destination IP set, and timestamped evidence bundle).
  • Temporary containment for unknown outbound SSH spikes must support immediate deny out 22/tcp policy with controlled exception handling.
  • Post-containment incident handling must include at least one same-day revalidation capture proving outbound TCP/22 is inactive and documenting current controls (UFW, sshd -T auth matrix, Fail2ban sshd jail status, and monitoring inventory).
  • Outbound TCP/22 policy must be finalized to one permanent model: deny-by-default or explicit destination allowlist with documented exception handling.
  • Permanent outbound SSH policy must keep deny out 22/tcp enforced on both IPv4 and IPv6.
  • Permanent outbound SSH deny rule must be logging-enabled so blocked outbound attempts are observable for alerting.
  • GitHub operations from VPS must use SSH over 443 (ssh.github.com) or HTTPS over 443; do not depend on outbound TCP/22 allowlists for GitHub IP ranges.
  • Temporary outbound SSH exceptions must be managed through script-based controls with mandatory audit fields: requester/approver, ticket or incident id, destination, reason and expected duration, and close time with verifier.
  • Temporary outbound SSH exceptions must default to a maximum 60-minute window unless explicitly overridden by owner approval.
  • Outbound SSH exception approval authority is restricted to single-owner authorization.
  • Blocked outbound SSH attempts must trigger immediate Discord alerting and append-only local policy logs.
  • SSH egress Discord notifications must use Markdown-formatted content (underlined heading plus ordered-list fields) for readability; HTML message formatting is not supported in webhook content.
  • SSH daemon policy must enforce key-based authentication only (AuthenticationMethods publickey) and disallow password/interactive authentication.
  • Root SSH access, if retained, must be key-only (PermitRootLogin prohibit-password) with reduced brute-force window (MaxAuthTries and LoginGraceTime hardening).
  • Root password must be rotated after host-level incident response actions that indicate credential exposure risk.
  • Provider remote console access (for example Contabo VNC) must remain disabled by default and enabled only for audited, time-boxed break-glass operations.
  • Post-incident access reset must include trusted-key rotation for host SSH, VS Code SSH, and Tabby SSH paths.
  • Fail2ban sshd jail must be active with systemd log backend and escalating bans for repeated brute-force sources.

Governance, Incident, and Recovery Controls

  • Production change execution authority remains single-owner only; approvals are explicit or executed manually by owner.
  • Production docker compose commands require explicit owner approval before execution.
  • Incident response workflow must be maintained in docs/operations/governance/incident-response-playbook.mdx and used for security/availability incidents.
  • Post-incident deep integrity validation workflow must be maintained in docs/operations/governance/host-integrity-checklist.mdx.
  • Production change governance workflow must be maintained in docs/operations/governance/change-control-policy.mdx.
  • Disaster recovery policy must be maintained in docs/operations/governance/disaster-recovery-plan.mdx.
  • Critical service recovery objectives are finalized at RTO 4 hours and RPO 1 hour.
  • Interim backup destination for stateful data is RustFS on the same VPS; this is temporary and does not replace offsite DR requirements.
  • Monthly restore validation drills are required and outcomes must be recorded with measured recovery time and recovery point.
  • Offsite backup destination definition and rollout remain mandatory before full production sign-off.
  • Offsite delivery uses OAuth-backed Google Drive remotes wrapped by rclone crypt; OAuth and crypt state must remain in writable root-only /etc/contabo-backups/rclone.conf, while operational policy remains in root-only /etc/contabo-backups/backup.env.
  • WordPress remote policy is last-day monthly at 03:00 UTC with four verified archives per site. Tier-1 database remote policy is Sunday 03:00 UTC with twelve verified remote sets and three local sets.
  • The weekly Tier-1 backup schedule does not satisfy the one-hour RPO and remains an explicit DR gap pending a separately approved higher-frequency recovery mechanism.
  • Shared Tier-1 database sets remain central-only. A client Drive may receive only explicitly mapped WordPress, service, or database data owned by that client.
  • Remote pruning and local staging cleanup must occur only after successful rclone cryptcheck; upload failure must preserve local artifacts and return a failed result.
  • Tier-1 database backup automation helper must be maintained at operations/backups/db-backup.sh with relative manifest paths, PostgreSQL globals and all connectable non-template databases (including postgres), all non-system MySQL databases, a MongoDB cluster dump, optional MSSQL user databases, and strict rolling retention.
  • The production Google Drive rollout may exclude MSSQL through a host-local ENGINES override while its service intentionally remains 0/0; do not start or scale MSSQL solely for backup validation without explicit approval, and restore MSSQL coverage when the service is re-enabled.
  • Weekly Tier-1 scheduling templates and their installer must be maintained under operations/systemd/tier1-db-backup/, using Sunday 03:00 UTC as the default cadence and a shared lock with WordPress backups.
  • Restore drill evidence should be captured with docs/operations/templates/restore-validation-template.mdx.
  • Production change execution evidence should be captured with docs/operations/templates/production-change-record-template.mdx.
  • Incident closure evidence should be captured with docs/operations/templates/incident-closure-template.mdx.
  • NetBird versus non-NetBird access evidence should be captured with docs/operations/governance/netbird-access-validation-matrix.mdx.
  • Approved production execution bundles and instantiated change records should be stored under docs/ (for example docs/operations/change-records/).
  • First-run backup timer evidence should be captured using operations/diagnostics/collect-tier1-backup-evidence.sh and attached to the executed change record.
  • Monthly PostgreSQL container-recovery backups must use the running container's native pg_basebackup, included WAL, tar/gzip, spread checkpoints, and SHA-256 native manifest. Raw archives of the live PostgreSQL Docker volume are prohibited.
  • PostgreSQL physical snapshots must keep the newest four verified remote and two local sets, share the host backup lock, and run on the first Sunday at 04:00 UTC only after the first snapshot passes an isolated restore.
  • PostgreSQL physical restore tooling must require new scratch-prefixed resources, refuse databases_postgres-data, use the recorded exact image/data layout/ownership, attach no production network, publish no port, and never update the Swarm service.
  • WordPress backup scope is exactly dbskc.com, gorsistudio.com, store.gorsistudio.com, and wcblahore.pk; Nishat variants are excluded.
  • All backup workflows must wait up to 21600 seconds on the shared lock, preserve failed staging, record sanitized evidence, and prune only after rclone cryptcheck succeeds.

Documentation and Operator Workflow

  • Maintain the current operator guide set under docs/runtime with a master runtime rebuild guide, per-stack guides, a service deployment guide, an environments guide, a CI guide, and a wrapper-script usage guide.
  • Keep swarm/README.md and docs/runtime/swarm-migration.mdx as the active deployment references; retain the Compose guides for rollback and local use.
  • Maintain cross-platform launchers under swarm/scripts for active Swarm stacks and under runtime/scripts for retained Compose deployment units.
  • Launcher scripts must prefer live runtime/environments/vps/**/.env files when present, fall back to tracked placeholder env files when live .env files are absent, and support dev templates for reproducible non-production runs.
  • When older planning or phase docs remain in docs/runtime, mark them as reference-only once a newer operator guide supersedes them.

Databases (Current Scope)

  • Repository database scope is MSSQL + PostgreSQL + MySQL + MongoDB. The current Google Drive activation covers PostgreSQL + MySQL + MongoDB while MSSQL intentionally remains 0/0; this is a temporary documented exception, not removal of MSSQL support.
  • MSSQL version target is SQL Server 2022.
  • MSSQL edition for the current VPS environment is intentionally MSSQL_PID=Developer (non-production licensing model for this rollout).
  • Friendly developer DB hostnames over NetBird are:
    • mssql.perspective-v.com
    • pgsql.perspective-v.com
    • mysql.perspective-v.com
    • mongo.perspective-v.com
  • DB/UI hostnames should remain public DNS records; a separate NetBird DNS zone is not required in this rollout model.
  • DB management UIs (pgAdmin, phpMyAdmin, mongo-express) must be routed through Traefik with NetBird allowlist and security headers.
  • pgAdmin route headers must allow same-origin framing (X-Frame-Options: SAMEORIGIN) so Query Tool views load correctly behind Traefik.
  • DB management UI authentication should be handled by each UI's built-in login; no shared Traefik admin-auth on these routes.
  • Mongo Express must keep built-in basic auth enabled in this model.
  • Deployed application containers must use internal Docker DB hostnames (mssql, postgres, mysql, mongodb) in runtime connection strings.
  • NetBird dashboard peer-readiness checks are mandatory before DB cutover (server peer and authorized developer peers connected).
  • Operators must record the active NetBird interface name and server NetBird IP before applying interface-scoped firewall cutover.
  • DB containers are long-lived stateful services.
  • DB update cadence is controlled monthly maintenance windows.
  • Controlled monthly DB maintenance window is scheduled for the first Saturday at 02:00 UTC.
  • DB major upgrades must be executed in staged maintenance windows with pre-change volume backups and explicit rollback checkpoints.
  • DB compose should use env-managed secrets, restart policies, and health checks.
  • Default DB bind model starts localhost-only, then can be rebound for NetBird-only access with UFW restrictions.
  • Compose artifact naming convention must use docker-compose.{service}.yml without environment suffixes.
  • Runtime env naming convention must use {service}.env for production values.
  • Production runtime env values for active VPS stacks must be stored under runtime/environments/vps.
  • Traefik ACME runtime state must be host-managed at /var/lib/traefik/letsencrypt/acme.json with file mode 600.
  • Non-production env template naming convention must use {service}.dev.env where validation workflows require non-production overlays.
  • Non-production env templates must be stored under runtime/environments/dev, while compose files remain under runtime/stacks.
  • Database env templates in non-production must use per-engine subfolders: runtime/environments/dev/infrastructure/databases/<engine>/<engine>.dev.env.
  • For active deployed VPS stacks, keep manifests under swarm/stacks and live environment values under runtime/environments/vps.

Update Automation Policy

  • Watchtower remains the only update automation tool in this phase.
  • Active Swarm Watchtower services must run in platform; the retained Compose equivalents remain under runtime/stacks/infrastructure/operations.
  • WUD is alerting-only and belongs to the active panel stack; its retained Compose equivalent remains in the operations compose unit.
  • WUD host route is wud.perspective-v.com behind Traefik with NetBird-only source allowlist.
  • WUD alert channel uses Discord webhook notifications.
  • WUD must include authenticated custom-registry configuration for https://registry.perspective-v.com so private-registry image checks are available.
  • WUD watcher mode for this rollout is discovery by default (WUD_WATCHER_LOCAL_WATCHBYDEFAULT=true) so all running containers are inventoried unless explicitly excluded.
  • After watcher-mode or label-policy changes, trigger an immediate WUD rescan with POST /api/containers/watch to avoid waiting for cron.
  • WUD-monitored services should define wud.tag.include guardrails to keep alerts on approved stable channels and avoid pre-release/noisy tag families.
  • WUD custom registry auth must use a dedicated non-admin credential (wud-monitor) so monitoring access is isolated from admin operations.
  • Approved current major targets for controlled rollout are: Verdaccio 6, Redis 8-alpine, RabbitMQ 4.2-management-alpine, and MySQL 9.6-oraclelinux9.
  • Diun remains deferred.
  • Auto-updates are allowed only on explicitly labeled stateless services.
  • platform must run two Watchtower services: baseline weekly (WATCHTOWER_DEFAULT_SCOPE=none, WATCHTOWER_SCHEDULE=0 0 3 * * 0) and fast scoped (WATCHTOWER_FAST_SCOPE=fast, WATCHTOWER_FAST_INTERVAL=3600).
  • Fast Watchtower must receive private Gitea registry credentials through a read-only Docker config mounted from a Swarm secret; do not pass registry credentials as Watchtower-wide REPO_USER/REPO_PASS values.
  • Stateless application services in runtime/stacks/services must set com.centurylinklabs.watchtower.enable=true to opt into automatic latest rollouts after initial bootstrap deployment.
  • All application services in runtime/stacks/services must set com.centurylinklabs.watchtower.scope=fast so they are targeted by the fast-scoped Watchtower instance.
  • Public-facing services in runtime/stacks/services must set Traefik labels for traefik.enable=true, traefik.docker.network=proxy, websecure entrypoint, TLS enabled, tls.certresolver=letsencrypt, security-headers@file, and explicit service load-balancer port labels.
  • Service stack containers in runtime/stacks/services must include runtime healthchecks using internal container ports (with safe defaults when env values are absent).
  • Healthcheck commands must be compatible with the image shell/tooling baseline; do not rely on bash in Alpine/nginx-based images that provide /bin/sh only.
  • Stateful services (databases, brokers, feeds, registry, object storage) stay on manual updates.
  • Baseline Watchtower update window is weekly Sunday 03:00 server time.

Gitea Unified Package Registry (Current — supersedes Private Container Registry + Package Feeds)

  • A single Gitea instance is the canonical registry for ALL packaging (Docker images, npm, NuGet); it is configured as a package-only registry with git features disabled (SSH off, registration disabled, no repos).
  • Gitea host is gitea.perspective-v.com behind edge Traefik TLS (Let's Encrypt), org owner for all packages is perspective-v.
  • Endpoints: Docker gitea.perspective-v.com/perspective-v/<img>, npm https://gitea.perspective-v.com/api/packages/perspective-v/npm/, NuGet https://gitea.perspective-v.com/api/packages/perspective-v/nuget/index.json.
  • Auth is Gitea user + access token: write:package scope to push (CI), read:package scope to pull (runtime + WUD); no Traefik basic-auth/htpasswd registry model.
  • Runtime service image references must resolve to gitea.perspective-v.com/perspective-v/*; the dedicated pull user is svc-puller (read:package), and Swarm updates use docker service update --with-registry-auth.
  • CI/CD image and package pipelines must authenticate to Gitea with a write:package token and push to the perspective-v namespace/endpoints above.
  • WUD must watch the Gitea registry using a dedicated wud-monitor Gitea user + read:package token.
  • The legacy per-subdomain registry/feed endpoints are RETIRED: registry.perspective-v.com, registry-admin.perspective-v.com, nuget.perspective-v.com (BaGet), npm.perspective-v.com (Verdaccio), proget.perspective-v.com (ProGet).
  • Gitea deploy/usage is documented in docs/runtime/stacks/gitea.mdx; legacy retirement in docs/runtime/gitea-retirement-runbook.mdx.

Private Container Registry (RETIRED 2026-07-01 — superseded by Gitea Unified Package Registry)

This section is historical. The private Docker Registry + Registry Admin (registry.perspective-v.com/registry-admin.perspective-v.com) were retired and replaced by the Gitea Docker package registry. Requirements below are kept for reference only.
  • Deploy a private Docker Registry (distribution) and Registry Admin.
  • Registry API is routed through Traefik as public HTTPS endpoint protected by HTTP basic auth middleware.
  • Registry API credentials must be sourced from shared host file /var/lib/traefik/registry-auth/registry.htpasswd.
  • Traefik must load registry auth from file-provider middleware (registry-basic-auth@file) instead of docker-label inline users.
  • Registry Admin must write users to the same shared htpasswd file so user creation in Registry Admin works for Docker login without registry service recreate.
  • Registry auth runbook must include Traefik auth-state refresh after Registry Admin credential changes; restart only the Traefik container (including restart from Portainer) when a newly updated user receives unexpected 401 Unauthorized despite correct htpasswd entry.
  • Registry API applies Traefik rate limiting with tunable average and burst values.
  • Registry Admin UI is routed through Traefik with NetBird source-range allowlist.
  • Registry Admin is the sole registry UI endpoint (legacy docker-registry-ui is retired).
  • Registry storage must persist on Docker volumes.
  • Registry authentication for Docker clients uses Traefik basic auth and supports hosted CI docker login workflow.
  • Registry Admin runtime connection to Registry must use internal Docker service URL (http://registry:5000) within the compose network.
  • registry-auth-proxy is not part of the runtime registry auth model.
  • Public token-realm dependency is removed from registry API access path.
  • Per-repository ACL enforcement is not provided by this auth model; CI credential separation and repository naming policy remain mandatory.
  • CI image pushes must produce Docker schema v2 compatible media types (non-OCI-only) so Registry Admin catalog sync remains operational.
  • Registry migration target must pin major v3 image in stack artifacts (registry:3) before production cutover execution.
  • Registry manifest deletion must remain enabled (REGISTRY_STORAGE_DELETE_ENABLED=true) for image cleanup workflows.
  • Repository cleanup must support tag/manifest deletion plus follow-up garbage collection to reclaim storage.
  • Repository purge is allowed through operator workflow (delete repository manifests, then run GC), with single-owner approval in this rollout model.
  • Registry cleanup cadence must run monthly, with dry-run preview before apply.
  • Registry cleanup must use a policy file that supports protected repositories/tags and monthly include/exclude selection.
  • Monthly cleanup policy must require explicit include targets before apply-mode execution.
  • Registry v3 cutover should pass a documented staging rehearsal sequence (auth challenge, authenticated API check, optional push/pull smoke check, delete dry-run, and monthly cleanup dry-run), unless explicitly waived by owner approval with pre-cutover backups and immediate post-cutover verification evidence.
  • Registry non-production dev environment template must be tracked at runtime/environments/dev/infrastructure/registry/registry.dev.env.

Object Storage (RustFS Baseline)

  • RustFS is the only supported object-storage runtime for this VPS phase.
  • MinIO is decommissioned from runtime due missing required self-hosted admin workflows in current community-mode behavior.
  • RustFS compose must be maintained at runtime/stacks/infrastructure/object-storage/rustfs/docker-compose.rustfs.yml.
  • RustFS console host must be rustfs.perspective-v.com behind NetBird-only Traefik policy and security headers.
  • RustFS API host must be rustfs-api.perspective-v.com behind NetBird-only Traefik policy and security headers.
  • RustFS authentication must use RustFS built-in credentials with explicit non-default RUSTFS_ACCESS_KEY and RUSTFS_SECRET_KEY values.
  • RustFS runtime image must be pinned to an explicit tag; latest is not allowed.
  • RustFS persistence must use host bind mounts at /var/lib/rustfs/data and /var/lib/rustfs/logs with UID/GID 10001:10001 ownership.
  • RustFS service-local runtime env must be maintained at runtime/environments/vps/infrastructure/object-storage/rustfs/.env.
  • RustFS tracked env templates must be maintained at runtime/environments/dev/infrastructure/object-storage/rustfs/rustfs.dev.env and runtime/environments/vps/infrastructure/object-storage/rustfs/rustfs.env.
  • Default bucket model is private; public object delivery should use app-issued signed URLs.
  • Object-storage-consuming services must use internal endpoint http://rustfs:9000 on the object-storage Docker network.
  • Runtime application env and service-stack artifacts must not reference MinIO hosts, MinIO internal endpoints, or MINIO_* variables.
  • If initial RustFS certificate issuance fails under Traefik strict SNI, a temporary sniStrict=false bootstrap fallback is allowed only for cert issuance and must be restored to sniStrict=true immediately after verification.
  • RustFS operations must remain within the current VPS resource envelope (4 vCPU, 8 GB RAM) without degrading existing baseline services.

Admin and Monitoring Scope

  • Admin and monitoring panel scope is Traefik dashboard, Portainer, and Kener.
  • Edge base stack artifacts must be split into runtime/stacks/infrastructure/edge/docker-compose.traefik.yml and runtime/stacks/infrastructure/edge/docker-compose.portainer.yml.
  • Kener runtime artifacts must be maintained at runtime/stacks/infrastructure/edge/docker-compose.kener.yml.
  • Kener host route must be kener.perspective-v.com behind edge Traefik TLS routing.
  • Traefik dashboard must remain NetBird-only and is protected by Traefik source-range allowlist and basic auth middleware.
  • Portainer must remain NetBird-only and use built-in panel authentication without shared Traefik admin-auth.
  • Portainer runtime image must remain Business Edition compatible with existing Portainer data (portainer/portainer-ee); CE downgrade requires an explicit migration path.
  • Kener route must be internet-accessible and use Kener built-in authentication without shared Traefik admin-auth.
  • Kener runtime must set KENER_SECRET_KEY, ORIGIN, and REDIS_URL; live secret values remain server-local runtime data only.
  • Kener SMTP runtime must set SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASSWORD, SMTP_FROM_EMAIL, and SMTP_SECURE from edge env.
  • Edge VPS env artifacts must be split into per-service folders: runtime/environments/vps/infrastructure/edge/traefik, runtime/environments/vps/infrastructure/edge/portainer, and runtime/environments/vps/infrastructure/edge/kener.
  • Edge non-production env templates must be split into per-service folders under runtime/environments/dev/infrastructure/edge/{traefik,portainer,kener} with {service}.dev.env naming.
  • Kener env keys must be maintained in runtime/environments/vps/infrastructure/edge/kener/.env (live runtime) and runtime/environments/vps/infrastructure/edge/kener/kener.env (tracked placeholder template).
  • Kener compose labels must keep a commented NetBird middleware fallback line so private-only rollback remains available.
  • Uptime Kuma is retired from active edge runtime in this phase.
  • Kener monitor migration model is manual recreation with critical monitors first.
  • Kener invitation-email flow is enabled via SMTP settings from edge env.
  • Keep monitoring lightweight for current VPS size; avoid heavy stacks until needed.
  • Lightweight monitoring baseline in this phase is Kener + WUD + Watchtower (watchtower and watchtower-fast) only.
  • Dozzle remains out of scope.

API Gateway (Current Scope)

  • Ocelot replacement path for this phase is Kong Gateway with Konga management UI.
  • Gateway artifacts must be maintained under runtime/stacks/infrastructure/gateway with compose naming docker-compose.kong.yml.
  • Kong metadata datastore must reuse existing PostgreSQL (postgres on postgres-network) with dedicated kong_db and kong_user credentials.
  • A separate Kong DB service/container is not part of this scope.
  • Kong proxy host for this phase is api.perspective-v.com behind edge Traefik TLS routing.
  • Konga host for this phase is konga.perspective-v.com and must remain NetBird-only with security headers.
  • Konga authentication should use Konga built-in login (no shared Traefik basic auth middleware).
  • Kong Admin API must remain internal-only on Docker network and must not be exposed on host ports.
  • Kong and backend upstream services must share the existing external proxy Docker network; do not create a dedicated gateway-only network for this phase.
  • Identity and Graph runtime stacks must be reachable as internal Kong upstream targets on Docker networking and must not be directly exposed by Traefik routers.
  • Console is a public Angular frontend and must be exposed directly by Traefik at console.perspective-v.com, not routed through Kong.
  • Dev gateway env template must be tracked at runtime/environments/dev/infrastructure/gateway/kong.dev.env with placeholder-only secrets.
  • VPS gateway env artifacts must be available at runtime/environments/vps/infrastructure/gateway/.env (live runtime source) and runtime/environments/vps/infrastructure/gateway/kong.env (tracked placeholder template).
  • Ocelot route parity must be implemented in Kong DB mode using managed services, routes, plugins, and consumer credentials visible in Konga.
  • Gateway migration artifacts must include runtime/stacks/infrastructure/gateway/ocelot-to-kong-mapping.md and runtime/stacks/infrastructure/gateway/kong-bootstrap-ocelot.sh.
  • Protected-route authentication parity must use Kong JWT plugin with key_claim_name=iss, claims_to_verify[]=exp, and a HS256 JWT credential keyed by issuer URL.
  • Gateway CORS policy must include AppCode, appcode, APPCODE, apollographql-client-name, and apollographql-client-version in allowed request headers.
  • Gateway CORS policy must include https://console.perspective-v.com and https://hassan.taj.contact in allowed origins for graph API browser access.
  • Gateway CORS policy must include https://hassantaj.github.io in allowed origins for graph API browser access.
  • Gateway route /graph/resume must allow OPTIONS in addition to POST so CORS preflight succeeds before browser API calls.
  • Graph resume access-token endpoint /graph/v1/resume/GetByAccessToken must be public and must not attach a JWT plugin.
  • Ocelot period limits must be represented in Kong using second-based local rate-limiting approximations.
  • Migrated Kong entities for Ocelot parity must be tagged ocelot-migration for operational audit visibility.
  • Gateway runtime must keep compatibility routes for client URL stability: /identity/docs/*, /identity/swagger/v1/swagger.json, and /swagger/v1/swagger.json must resolve to identity docs/swagger (/swagger/1.0/swagger.json alias path), and /resume must remain a UI passthrough route.
  • Gateway runtime must keep graph routing aligned with application middleware: /resume and /gopher/* are UI paths and must be passed through as-is (not rewritten to /graph/*), while /graph/gopher/{credential,serviceprovider,serviceprovideremail,platform} must resolve through explicit public routes (before graph-protected) to avoid route-miss/JWT-fallback regressions.
  • Gateway runtime must keep additive graph parity endpoints: /graph/docs and /graph/docs/* as public docs passthrough (/docs* rewrite), and /graph/v1/{endpoint} as JWT-protected identity-style versioned API passthrough (/api/v1/{endpoint} rewrite) with methods GET,POST,PUT,DELETE,OPTIONS.
  • Gateway stack must maintain declarative state file runtime/stacks/infrastructure/gateway/kong.yml compatible with decK gateway diff/sync automation workflows.

Platform and Application Requirements (Retained)

  • Workloads to migrate include .NET, Angular, React, Node.js/NestJS, package feeds, and static sites.
  • dbskc static website deployment must use runtime/stacks/services/dbskc.com/dbskc.com/docker-compose.services.yml.
  • dbskc public host route must be dbskc.com behind edge Traefik TLS.
  • dbskc runtime image reference must resolve to registry.perspective-v.com/dbskc-web:latest for Watchtower latest-rollout compatibility.
  • dbskc service must set com.centurylinklabs.watchtower.scope=fast for near-immediate updates through the fast scoped Watchtower instance.
  • dbskc service internal port for Traefik load-balancer mapping must be 80 for the nginx runtime image.
  • nishatcolony deployment must use runtime/stacks/services/nishatcolony.pk/nishatcolony.pk/docker-compose.services.yml.
  • nishatcolony public host route must be nishatcolony.pk behind edge Traefik TLS.
  • nishatcolony runtime image reference must resolve to registry.perspective-v.com/nishatcolony-web:latest.
  • nishatcolony service must set com.centurylinklabs.watchtower.scope=fast for near-immediate updates through the fast scoped Watchtower instance.
  • nishatcolony service internal port for Traefik load-balancer mapping must be 80 for the nginx runtime image.
  • Migration execution remains phased.
  • Reverse proxy TLS should use Let's Encrypt with TLS challenge on 443.
  • Let's Encrypt contact remains notifications at perspective-v.com.
  • Hybrid CI/CD remains the deployment model:
    • GitHub Actions for GitHub repositories.
    • Azure Pipelines for Azure DevOps repositories.
  • Service project naming conventions remain:
    • Databases: Databases
    • Platform: Platform
    • Operations: Operations
    • Feeds: Feeds
    • NetBird: NetBird
    • All service compose files under runtime/stacks/services must use project name perspective-v.
  • Shared service stack rollout order in this phase must be: Platform -> Operations -> Registry -> Feeds.
  • Platform stack scope in this phase is Redis + RabbitMQ + Redis Insight.
  • Developer-friendly Redis hostname over NetBird is redis.perspective-v.com.
  • Redis Insight hostname is redis-insight.perspective-v.com and must remain NetBird-only with no Traefik basic-auth middleware.
  • Production service containers must continue using internal Docker Redis hostname redis.
  • Platform Redis usage is restricted to cache/session/ephemeral coordination.
  • Critical source-of-truth data must not rely on Platform Redis.
  • Any durable critical Redis workload must be migrated to a dedicated stateful Redis stack with backup/restore testing before production sign-off.
  • Production service image references should use private registry host paths.
  • Services that consume object storage must attach to external object-storage network.

CI/CD Template Requirements

Registry/endpoint update 2026-07-01: CI image/package pipelines now push to the Gitea Unified Package Registry (gitea.perspective-v.com/perspective-v/* for Docker, /api/packages/perspective-v/{npm,nuget}/ for packages) using a Gitea write:package token. The registry.perspective-v.com/* targets and NPM_FEED_*/NUGET_FEED_*/REGISTRY_USERNAME/REGISTRY_PASSWORD contracts below are retired/historical; templates in runtime/ci/** were repointed accordingly.
  • Keep reusable CI templates under runtime/ci for both GitHub Actions and Azure Pipelines.
  • Provide service-specific templates for split repositories (identity, graph, console) in both CI systems.
  • Provide dbskc templates in both CI systems as build-and-push-dbskc.yml.
  • Provide nishatcolony templates in both CI systems as build-and-push-nishatcolony.yml.
  • Azure pipeline registry login steps must use docker login --password-stdin and exit with a clear error when registry secrets are empty.
  • Keep a generic build-and-push-single-image template for new or non-standard services.
  • dbskc CI templates must auto-trigger only for branch deploy/dbskc updates.
  • dbskc CI templates must push registry.perspective-v.com/dbskc-web with latest and v1.0.<run> tags.
  • nishatcolony CI templates must auto-trigger only for branch deploy/nishatcolony updates.
  • nishatcolony CI templates must push registry.perspective-v.com/nishatcolony-web with latest and v1.0.<run> tags.
  • identity CI templates must auto-trigger only for branch deploy/identity updates.
  • graph CI templates must auto-trigger only for branch deploy/graph updates.
  • console CI templates must auto-trigger only for branch deploy/console updates.
  • CI image workflows must authenticate to registry.perspective-v.com using secret credentials.
  • CI image workflows should publish both moving latest tags and immutable v1.0.<run> tags.
  • CI image workflows should use one repository per service name (for example, registry.perspective-v.com/api-gateway).
  • Buildx-based CI workflows must set compatibility flags for registry-admin (oci-mediatypes=false, and disable provenance/SBOM attestations when they force OCI-only manifests).

Package Feed Requirements (RETIRED 2026-07-01 — superseded by Gitea Unified Package Registry)

This section is historical. The Feeds stack (BaGet nuget.perspective-v.com + Verdaccio npm.perspective-v.com) was retired and replaced by Gitea npm/NuGet package endpoints (/api/packages/perspective-v/{npm,nuget}/) with Gitea token auth. Requirements below are kept for reference only.
  • Feed stack runs BaGet + Verdaccio as canonical package services.
  • Canonical NuGet endpoint is https://nuget.perspective-v.com.
  • Canonical npm endpoint is https://npm.perspective-v.com.
  • VPS feed retirement apply operations must recreate the feeds stack with canonical compose and --remove-orphans so legacy feed containers are removed from runtime.
  • Legacy feed artifact deletion on VPS must be rollback-aware: capture volume snapshot + checksum under tmp/backups/feeds-proget-retirement-<timestamp> before removing legacy volume/image remnants.
  • In live service-local feed runtime env (runtime/environments/vps/infrastructure/feeds/.env), secret values containing dollar signs must escape dollars as $$ to avoid Docker Compose interpolation truncation.
  • Feed restore/install endpoints must remain internet-accessible for CI and developer workflows.
  • BaGet must keep public restore/download behavior and API-key publish model.
  • Verdaccio must enforce no self-signup (auth.htpasswd.max_users=-1).
  • Verdaccio must keep anonymous read/install (access: $all) and authenticated publish/unpublish ($authenticated).
  • npm publish operations must be restricted to CI-only service accounts provisioned manually.
  • npm CI contracts should use NPM_FEED_URL/NPM_FEED_API_KEY (API key passed as npm auth token).
  • Feed environment artifacts must expose NPM_FEED_API_KEY in runtime/environments/dev/infrastructure/feeds/feeds.dev.env, runtime/environments/vps/infrastructure/feeds/feeds.env, and live runtime/environments/vps/infrastructure/feeds/.env for CI secret handoff.
  • NuGet CI contracts should use NUGET_FEED_URL/NUGET_FEED_TOKEN (token value mapped to BaGet API key).
  • SSO for feeds is deferred to a later phase.

Tracking Rules

  • already-implemented.md must list only tasks actually completed on server.
  • next-steps.md must stay as phased execution checklist.
  • accepted-suggestions.md must contain accepted decisions and supersede conflicting legacy choices.
  • Active production deployment artifacts must be kept under swarm/stacks/; retained Compose artifacts stay under runtime/stacks/.
  • Active VPS runtime env values must be kept under runtime/environments/vps/.
  • Git must track named env files under runtime/environments/ ({service}.env, *.dev.env) while plain .env files remain ignored.
  • Password and secret values in tracked named env files must use example placeholders only; real credentials must be set only in server-local runtime copies.
  • Live VPS compose executions must use service-local runtime/environments/vps/**/.env files as runtime source of truth; tracked runtime/environments/vps/**/{service}.env files are placeholders only.
  • Perspective-V service env files must be mirrored by service under runtime/environments/vps/services/perspective-v.com/<service-domain>/ and runtime/environments/dev/services/perspective-v.com/<service-domain>/.
  • For each Perspective-V service, tracked placeholders must use <service-domain>.env (VPS) and <service-domain>.dev.env (dev), while live VPS runtime values for compose execution must stay in service-local .env files.
  • Edge stack compose executions must use service-local env files under runtime/environments/vps/infrastructure/edge/{traefik,portainer,kener}/.env as runtime source of truth; tracked placeholders remain {service}.env in the same folders.
  • For PostgreSQL stacks with persisted volumes, operator runbook must include post-recreate credential alignment (ALTER ROLE postgres PASSWORD ...) when .env and live role password drift is detected.
  • For MySQL stacks with persisted volumes, operator runbook must include controlled recovery-mode credential alignment when live users drift from .env after recreate/upgrade operations.
  • Database runbook docs must include explicit compose teardown commands to stop/remove DB and DB UI containers without deleting volumes by default.
  • Documentation must be centralized under docs/; no legacy markdown runbooks/docs should be maintained under removed docs/ or under prod/.
  • Security incident records (provider replies, timelines, and evidence exports) must be archived under Incidents/<date-time>-<incident-name>/ with markdown and raw evidence artifacts together.

Docker Swarm Migration

  • Migrate from docker compose to Docker Swarm mode for orchestration, encrypted secrets, and service reconciliation.
  • All Swarm deployment artifacts must live under top-level swarm/; retained Compose files stay under runtime/, with compatibility wrappers allowed when canonical host tooling moves to operations/.
  • Docker Secrets must replace .env file passwords for all sensitive values (30+ secrets).
  • Five WordPress stacks must use pre-built images pushed to the private registry (registry.perspective-v.com/<name>:latest).
  • Traefik must switch from Docker socket provider to Swarm API provider with file-based middlewares (@file).
  • Seven external networks must convert from bridge to overlay (attachable).
  • All compose files must be normalized for Swarm (no container_name, deploy.restart_policy instead of restart, labels under deploy.labels, no depends_on.condition).
  • Launcher scripts must use docker stack deploy commands via a new swarm/scripts/ directory.
  • Rollback path must preserve compose-based deployment via runtime/scripts/.
  • Single-node Swarm only — multi-node and NFS/CSI are out of scope for this migration.
  • Independently scalable administration UIs must run under panel, carry com.perspective-v.role=panel, and support only zero/one replica suspend/resume controls.
  • Kener must remain in edge; Vaultwarden, Gitea, and RustFS must run in service, while Zitadel remains in service-zitadel.
  • Each manifest's first-line # Swarm stack: value is authoritative; all fragments with the same value must be rendered and deployed together.
  • Stack identity migrations must preserve existing external volume, network, secret, hostname, and access-policy contracts and must not run concurrently with their prior identities.
  • Active Swarm source must use the six role directories databases, platform, edge, services, panels, and websites.
  • Every service in a multi-service stack must have its own YAML fragment. Launchers must always render and deploy the complete fragment list.
  • Redis, RabbitMQ, Watchtower, Kong, NetBird, RustFS, Panels, Traefik, and Kener must use the stack/service identities documented in swarm/README.md.
  • Host-wide operational executables must use top-level operations/; workload bootstrap and configuration helpers remain beside their owning workload.
  • While MSSQL remains intentionally 0/0, the scheduled Tier-1 backup unit must explicitly target PostgreSQL, MySQL, and MongoDB; MSSQL is added only when the service is deliberately re-enabled.
  • Pi-hole administration must use pihole.home.perspective-v.com through the existing Contabo TLS edge, remain protected by netbird-only, and reach only the homelab's NetBird-bound dashboard at 100.83.117.37:8053.
  • Home Assistant at assistant.home.perspective-v.com may resolve publicly to Contabo's public edge, but access must remain guarded by Traefik netbird-only. Proxy only to the homelab NetBird listener at 100.83.117.37:8123; add only TCP 8123 to the existing peer policy.
  • NetBird peers use the private DNS answer 100.83.72.162; the public A record points to Contabo's public IP 161.97.83.142 for TLS issuance. Public DNS must not make Home Assistant accessible without NetBird.
  • Contabo documentation must keep Namecheap public DNS and Contabo Traefik as the public source of truth while documenting Pi-hole overrides as a LAN-only optimization to 192.168.1.135.
  • Shared LAN/public hostnames must retain the existing HTTPS Host routing and certificate contract; the restricted homelab certificate exporter must remain root-only, fail closed, and keep archives out of Git.

On this page