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/.envso AUTH/OIDC and management endpoint variables are present after image updates. - NetBird embedded IdP dashboard auth must keep
AUTH_CLIENT_SECRETempty 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
wwwandlearnon 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 (
wt0or 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/tcppolicy 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 -Tauth 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/tcpenforced 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 (MaxAuthTriesandLoginGraceTimehardening). - 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.mdxand 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.shwith relative manifest paths, PostgreSQL globals and all connectable non-template databases (includingpostgres), 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
ENGINESoverride while its service intentionally remains0/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 exampledocs/operations/change-records/). - First-run backup timer evidence should be captured using
operations/diagnostics/collect-tier1-backup-evidence.shand 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, andwcblahore.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 cryptchecksucceeds.
Documentation and Operator Workflow
- Maintain the current operator guide set under
docs/runtimewith 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.mdanddocs/runtime/swarm-migration.mdxas the active deployment references; retain the Compose guides for rollback and local use. - Maintain cross-platform launchers under
swarm/scriptsfor active Swarm stacks and underruntime/scriptsfor retained Compose deployment units. - Launcher scripts must prefer live
runtime/environments/vps/**/.envfiles when present, fall back to tracked placeholder env files when live.envfiles are absent, and supportdevtemplates 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.compgsql.perspective-v.commysql.perspective-v.commongo.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-authon 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}.ymlwithout environment suffixes. - Runtime env naming convention must use
{service}.envfor 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.jsonwith file mode600. - Non-production env template naming convention must use
{service}.dev.envwhere validation workflows require non-production overlays. - Non-production env templates must be stored under
runtime/environments/dev, while compose files remain underruntime/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/stacksand live environment values underruntime/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 underruntime/stacks/infrastructure/operations. - WUD is alerting-only and belongs to the active
panelstack; its retained Compose equivalent remains in the operations compose unit. - WUD host route is
wud.perspective-v.combehind 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.comso 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/watchto avoid waiting for cron. - WUD-monitored services should define
wud.tag.includeguardrails 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, Redis8-alpine, RabbitMQ4.2-management-alpine, and MySQL9.6-oraclelinux9. - Diun remains deferred.
- Auto-updates are allowed only on explicitly labeled stateless services.
platformmust 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_PASSvalues. - Stateless application services in
runtime/stacks/servicesmust setcom.centurylinklabs.watchtower.enable=trueto opt into automaticlatestrollouts after initial bootstrap deployment. - All application services in
runtime/stacks/servicesmust setcom.centurylinklabs.watchtower.scope=fastso they are targeted by the fast-scoped Watchtower instance. - Public-facing services in
runtime/stacks/servicesmust set Traefik labels fortraefik.enable=true,traefik.docker.network=proxy,websecureentrypoint, TLS enabled,tls.certresolver=letsencrypt,security-headers@file, and explicit service load-balancer port labels. - Service stack containers in
runtime/stacks/servicesmust 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
bashin Alpine/nginx-based images that provide/bin/shonly. - 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.combehind edge Traefik TLS (Let's Encrypt), org owner for all packages isperspective-v. - Endpoints: Docker
gitea.perspective-v.com/perspective-v/<img>, npmhttps://gitea.perspective-v.com/api/packages/perspective-v/npm/, NuGethttps://gitea.perspective-v.com/api/packages/perspective-v/nuget/index.json. - Auth is Gitea user + access token:
write:packagescope to push (CI),read:packagescope 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 issvc-puller(read:package), and Swarm updates usedocker service update --with-registry-auth. - CI/CD image and package pipelines must authenticate to Gitea with a
write:packagetoken and push to theperspective-vnamespace/endpoints above. - WUD must watch the Gitea registry using a dedicated
wud-monitorGitea user +read:packagetoken. - 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 indocs/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 Unauthorizeddespite 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-proxyis 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.combehind NetBird-only Traefik policy and security headers. - RustFS API host must be
rustfs-api.perspective-v.combehind NetBird-only Traefik policy and security headers. - RustFS authentication must use RustFS built-in credentials with explicit non-default
RUSTFS_ACCESS_KEYandRUSTFS_SECRET_KEYvalues. - RustFS runtime image must be pinned to an explicit tag;
latestis not allowed. - RustFS persistence must use host bind mounts at
/var/lib/rustfs/dataand/var/lib/rustfs/logswith UID/GID10001:10001ownership. - 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.envandruntime/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:9000on theobject-storageDocker 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=falsebootstrap fallback is allowed only for cert issuance and must be restored tosniStrict=trueimmediately 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.ymlandruntime/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.combehind 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, andREDIS_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, andSMTP_SECUREfrom 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, andruntime/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.envnaming. - Kener env keys must be maintained in
runtime/environments/vps/infrastructure/edge/kener/.env(live runtime) andruntime/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 (
watchtowerandwatchtower-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/gatewaywith compose namingdocker-compose.kong.yml. - Kong metadata datastore must reuse existing PostgreSQL (
postgresonpostgres-network) with dedicatedkong_dbandkong_usercredentials. - A separate Kong DB service/container is not part of this scope.
- Kong proxy host for this phase is
api.perspective-v.combehind edge Traefik TLS routing. - Konga host for this phase is
konga.perspective-v.comand 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
proxyDocker 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.envwith placeholder-only secrets. - VPS gateway env artifacts must be available at
runtime/environments/vps/infrastructure/gateway/.env(live runtime source) andruntime/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.mdandruntime/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, andapollographql-client-versionin allowed request headers. - Gateway CORS policy must include
https://console.perspective-v.comandhttps://hassan.taj.contactin allowed origins for graph API browser access. - Gateway CORS policy must include
https://hassantaj.github.ioin allowed origins for graph API browser access. - Gateway route
/graph/resumemust allowOPTIONSin addition toPOSTso CORS preflight succeeds before browser API calls. - Graph resume access-token endpoint
/graph/v1/resume/GetByAccessTokenmust 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-migrationfor operational audit visibility. - Gateway runtime must keep compatibility routes for client URL stability:
/identity/docs/*,/identity/swagger/v1/swagger.json, and/swagger/v1/swagger.jsonmust resolve to identity docs/swagger (/swagger/1.0/swagger.jsonalias path), and/resumemust remain a UI passthrough route. - Gateway runtime must keep graph routing aligned with application middleware:
/resumeand/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 (beforegraph-protected) to avoid route-miss/JWT-fallback regressions. - Gateway runtime must keep additive graph parity endpoints:
/graph/docsand/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 methodsGET,POST,PUT,DELETE,OPTIONS. - Gateway stack must maintain declarative state file
runtime/stacks/infrastructure/gateway/kong.ymlcompatible with decKgateway diff/syncautomation 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.combehind edge Traefik TLS. - dbskc runtime image reference must resolve to
registry.perspective-v.com/dbskc-web:latestfor Watchtower latest-rollout compatibility. - dbskc service must set
com.centurylinklabs.watchtower.scope=fastfor near-immediate updates through the fast scoped Watchtower instance. - dbskc service internal port for Traefik load-balancer mapping must be
80for 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.pkbehind 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=fastfor near-immediate updates through the fast scoped Watchtower instance. - nishatcolony service internal port for Traefik load-balancer mapping must be
80for 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/servicesmust use project nameperspective-v.
- Databases:
- 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.comand 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-storagenetwork.
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/cifor 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-stdinand exit with a clear error when registry secrets are empty. - Keep a generic
build-and-push-single-imagetemplate for new or non-standard services. - dbskc CI templates must auto-trigger only for branch
deploy/dbskcupdates. - dbskc CI templates must push
registry.perspective-v.com/dbskc-webwithlatestandv1.0.<run>tags. - nishatcolony CI templates must auto-trigger only for branch
deploy/nishatcolonyupdates. - nishatcolony CI templates must push
registry.perspective-v.com/nishatcolony-webwithlatestandv1.0.<run>tags. - identity CI templates must auto-trigger only for branch
deploy/identityupdates. - graph CI templates must auto-trigger only for branch
deploy/graphupdates. - console CI templates must auto-trigger only for branch
deploy/consoleupdates. - CI image workflows must authenticate to
registry.perspective-v.comusing secret credentials. - CI image workflows should publish both moving
latesttags and immutablev1.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-orphansso 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_KEYinruntime/environments/dev/infrastructure/feeds/feeds.dev.env,runtime/environments/vps/infrastructure/feeds/feeds.env, and liveruntime/environments/vps/infrastructure/feeds/.envfor 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.mdmust list only tasks actually completed on server.next-steps.mdmust stay as phased execution checklist.accepted-suggestions.mdmust contain accepted decisions and supersede conflicting legacy choices.- Active production deployment artifacts must be kept under
swarm/stacks/; retained Compose artifacts stay underruntime/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.envfiles 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/**/.envfiles as runtime source of truth; trackedruntime/environments/vps/**/{service}.envfiles are placeholders only. - Perspective-V service env files must be mirrored by service under
runtime/environments/vps/services/perspective-v.com/<service-domain>/andruntime/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.envfiles. - Edge stack compose executions must use service-local env files under
runtime/environments/vps/infrastructure/edge/{traefik,portainer,kener}/.envas runtime source of truth; tracked placeholders remain{service}.envin the same folders. - For PostgreSQL stacks with persisted volumes, operator runbook must include post-recreate credential alignment (
ALTER ROLE postgres PASSWORD ...) when.envand 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
.envafter 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 removeddocs/or underprod/. - 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 composeto Docker Swarm mode for orchestration, encrypted secrets, and service reconciliation. - All Swarm deployment artifacts must live under top-level
swarm/; retained Compose files stay underruntime/, with compatibility wrappers allowed when canonical host tooling moves tooperations/. - Docker Secrets must replace
.envfile 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
bridgetooverlay(attachable). - All compose files must be normalized for Swarm (no
container_name,deploy.restart_policyinstead ofrestart, labels underdeploy.labels, nodepends_on.condition). - Launcher scripts must use
docker stack deploycommands via a newswarm/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, carrycom.perspective-v.role=panel, and support only zero/one replica suspend/resume controls. - Kener must remain in
edge; Vaultwarden, Gitea, and RustFS must run inservice, while Zitadel remains inservice-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, andwebsites. - 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.comthrough the existing Contabo TLS edge, remain protected bynetbird-only, and reach only the homelab's NetBird-bound dashboard at100.83.117.37:8053. - Home Assistant at
assistant.home.perspective-v.commay resolve publicly to Contabo's public edge, but access must remain guarded by Traefiknetbird-only. Proxy only to the homelab NetBird listener at100.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 IP161.97.83.142for 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.