R940 Naming Convention v1 (NAMING-3)¶
Status: Canonical as of 2026-08-29 (NAMING-2 executed).
Supersedes: Ad-hoc *-r940 / *-supabase hostnames and {PRODUCT}_R940_PROD_* BWS prefixes.
Cross-reference: R940_PRODUCT_ISOLATION_PRINCIPLES_v1.md
1. Design intent¶
All product workloads on the R940 Proxmox cluster use role-based names (-app, -db) instead of location suffixes (-r940) or vendor labels (-supabase). The hypervisor is R940 by definition; self-hosted database stacks replace hosted Supabase per product.
Infrastructure VMs (identity, observability, edge connectors with dedicated tunnel names) keep their existing names unless Ryan directs otherwise.
2. VM naming¶
| Role | Pattern | Examples |
|---|---|---|
| Product application | {product}-app |
admin-app, realoutcomes-app, onset-app, n8n-app |
| Product database / stack | {product}-db |
realoutcomes-db, onset-db |
| Single-VM product | {product}-app |
n8n-app (was n8n-r940) |
| Identity | auth-last1id |
unchanged |
| Identity DB | sovereign-db-last1id |
unchanged |
| Observability | observability |
unchanged |
| Dedicated edge tunnels | lastvet-edge, last1id-edge |
tunnel names unchanged |
| Product edge tunnels | {product}-edge |
tunnel name unchanged; runs on {product}-app |
Proxmox: VM display name matches guest hostname. Cloud-init snippets: {product}-app-user.yaml, {product}-db-user.yaml.
LAN IPs: Stable; see R940_LAN_NETWORK_INVENTORY.md. Never assign 192.168.1.100 (iDRAC/BMC).
3. BWS secret prefixes¶
| Scope | Pattern | Example |
|---|---|---|
| App-tier prod secrets | {PRODUCT}_APP_PROD_{KEY} |
REALOUTCOMES_APP_PROD_SESSION_SECRET |
| DB-tier prod secrets | {PRODUCT}_DB_PROD_{KEY} |
REALOUTCOMES_DB_PROD_DATABASE_URL |
| Edge (on app VM) | {PRODUCT}_APP_EDGE_{KEY} |
REALOUTCOMES_APP_EDGE_TUNNEL_ID |
| Platform n8n | N8N_APP_PROD_{KEY} in last1-platform project |
N8N_APP_PROD_ENCRYPTION_KEY |
DB vs APP split (required for {product}-app + {product}-db products)¶
{PRODUCT}_DB_PROD_* (stack / Postgres / Kong):
DATABASE_URL,SUPABASE_URL,SUPABASE_POOLER_URLPOSTGRES_PASSWORD,JWT_SECRETSUPABASE_ANON_KEY,SUPABASE_SERVICE_ROLE_KEY
{PRODUCT}_APP_PROD_* (application runtime, build, integrations):
- Stripe, SendGrid, OAuth, session secrets,
PUBLIC_URL, cron secrets VITE_SUPABASE_*,NEXT_PUBLIC_SUPABASE_*(build-time; values mirror DB stack)
App-only products (e.g. admin-app, n8n-app): all secrets under {PRODUCT}_APP_PROD_* only.
Out of scope (separate conventions)¶
LAST1ID_*/LAST1_ID_*— identity (auth-last1id)LASTVET_*— LastVet platformLAST1_PLATFORM_*— shared platform ops (exceptN8N_APP_*)
4. DNS and staging¶
| Purpose | Pattern |
|---|---|
| Staging app | {product}-app.last1.enterprises |
| Staging DB / Kong (browser auth E2E) | {product}-db.last1.enterprises |
| Production | Product domain (realoutcomes.app, clinic.theonset.app, etc.) |
Production DNS is not tied to VM hostname. Cloudflare tunnel ingress uses origin IPs or LAN URLs; tunnel names (realoutcomes-edge, onset-edge) stay stable.
5. Observability labels¶
| Layer | Pattern |
|---|---|
| Health probe metrics | {product}_app_probe_* |
| Supabase reachability | {product}_app_supabase_reachable |
| Loki host label | {product}-app |
| Prometheus node scrape | By IP (stable across renames) |
Alert names keep product semantics (RealOutcomesHostDown, AdminHealthProbeFailed); annotations reference new hostnames.
6. Scripts and deploy artifacts¶
| Artifact | Pattern |
|---|---|
| Deploy script | deploy-{product}-app.sh |
| Env render | render-{product}-env-app.sh (reads APP + DB prefixes) |
| Stack → BWS | push-db-stack-bws.py |
| Railway sync | sync-app-bws-from-railway.py (skips DB keys) |
| Docker image tag | {product}-app:prod |
7. Future migrations¶
Applies to:
- last1.app + RadioCheck bundle (admin reconnect)
- MATR and any new R940 product
- Remaining Tier work after Onset T-3
Checklist for new product:
- Provision
{product}-app(+{product}-dbif self-hosted stack) - Create BWS project or prefix family:
{PRODUCT}_APP_PROD_*,{PRODUCT}_DB_PROD_* - Product edge tunnel
{product}-edgeon app VM - Prometheus scrape by IP; probe metrics
{product}_app_probe_* - No
-r940or-supabasein any new name
8. Rename history¶
Full inventory and execution log: VM_NAMING_RENAME_AUDIT.md (NAMING-1 approved 2026-08-29; NAMING-2 executed same day).
| VMID | Old | New |
|---|---|---|
| 112 | n8n-r940 | n8n-app |
| 115 | admin-r940 | admin-app |
| 116 | realoutcomes-r940 | realoutcomes-app |
| 117 | realoutcomes-supabase | realoutcomes-db |
| 118 | onset-r940 | onset-app |
| 119 | onset-supabase | onset-db |
9. Operational lessons (post-NAMING-2)¶
Discovered during RealOutcomes directory 500 regression (REALOUTCOMES_DIRECTORY_500_DIAGNOSTIC.md). Apply to every product migration and BWS prefix rename.
9.1 BWS copy ≠ validated DB connection¶
When copying {PRODUCT}_R940_* → {PRODUCT}_APP_* / {PRODUCT}_DB_*, values carry stale references forward (e.g. deleted hosted Supabase pooler URLs). Never trust copied DATABASE_URL / SUPABASE_URL without re-validation.
Canonical source for DB connection strings: live {product}-db VM via push-db-stack-bws.py, not BWS from an earlier migration step or Railway sync.
Required after any BWS prefix rename or key copy:
- Run
scripts/{product}/push-db-stack-bws.pyagainst live{product}-db. - Confirm
{PRODUCT}_DB_PROD_DATABASE_URLhost is{product}-dbLAN IP (or pooler on:6543), not*.supabase.cooraws-*-pooler.supabase.com(unless product is intentionally hosted-only, e.g. admin until Tier 6). - Re-render app
.envfrom APP + DB prefixes.
9.2 Docker env reload¶
docker restart does not re-read --env-file. Env is fixed at docker run / container create time.
After any .env change: remove and recreate the container (or run deploy-{product}-app.sh). Never rely on restart alone for connection-string fixes.
9.3 Product classes¶
| Class | DB BWS target | Example |
|---|---|---|
Sovereign stack ({product}-app + {product}-db) |
{PRODUCT}_DB_PROD_* → LAN {product}-db |
RealOutcomes, Onset |
| Downstream BFF (no own DB VM) | {PRODUCT}_APP_PROD_* → hosted Supabase until upstream migrates |
last1-admin (paused, Tier 6) |
| Gate B / migration-only | Separate key (e.g. ONSET_HOSTED_DATABASE_URL) |
Hosted dump source; not app runtime |
See R940_MIGRATION_PLAYBOOK.md for step-by-step checklist.