S282.3 Infrastructure Rename Handoff
Documentation for Sessions
S282.3 Handoff: Infrastructure DNS Rename — sandbox.gograb.space → ax42-01.gograb.space
Session: 282.3 | Date: 2026-03-28 | Branch: session-280-285
Status: Complete — runtime code, server-side, Cloudflare, and Railway all updated
1. What Happened & Why
We operate two Hetzner dedicated servers. The old server (AX42-U, 144.76.69.55) was historically named sandbox.gograb.space. The new server (AX102-U, 178.63.165.83) follows the convention ax102-01.gograb.space. Having one server named sandbox.* and the other ax102-01.* caused persistent confusion — config bugs, wrong healthUrls, agents misrouting.
S282.3 fixed two bugs:
SELF_HOSTED_INFRA_JSONhadhealthUrlpointing toax102-01.gograb.spaceinstead of AX42's URL → dedup logic inHetznerInfraService.getSelfHostedEntries()silently dropped AX42 from the dashboardgit.service.tshardcoded fallback still pointed tosandbox.gograb.space→ should beax102-01.gograb.space(git repos moved to AX102 in S260)
S282.3 renamed all runtime code references (11 files) and cleaned up .env.local comments (documentation moved here).
2. Authoritative Server Reference
| Server | DNS | IP | API Key | Roles |
|---|---|---|---|---|
| AX102-U | ax102-01.gograb.space |
178.63.165.83 | hNwl3fka8Jc_xuUFvdvWin2IPMfI38sL5XuSpKQD1MI |
customer-sandbox (priority 1), git-operations |
| AX42-U | ax42-01.gograb.space (was sandbox.gograb.space) |
144.76.69.55 | W8Wp0ltL8El_qRnXyoUoD0ffuSfZzmbn7Y7SVrx1k8s |
executive-sandbox, customer overflow (priority 2) |
Server Details
| Field | AX42-U | AX102-U |
|---|---|---|
| Hardware | AX42-U #2938512 | AX102-U #2946196 |
| CPU | Ryzen 7 PRO 8700GE (8C/16T) | Ryzen 9 7950X3D (16C/32T) |
| RAM | 128GB DDR5 | 128GB DDR5 |
| Storage | 2x512GB NVMe | 2x1TB NVMe |
| Datacenter | FSN1-DC10 | FSN1-DC21 |
| VM Capacity | 30 | 60 |
| SSH | ssh [email protected] (key auth) |
ssh [email protected] |
Environment Variable Mapping
| Variable | Value | Why |
|---|---|---|
SANDBOX_SERVERS_JSON |
Both servers with roles | PlacementRouter multi-server config |
SANDBOX_SERVER_URL |
https://ax102-01.gograb.space |
Primary sandbox server (legacy single-server fallback) |
SANDBOX_API_KEY |
AX102-U's key | Matches SANDBOX_SERVER_URL |
SANDBOX_SERVER_LABEL |
AX102-U (Falkenstein DC21) |
Dashboard label for primary |
SANDBOX_SERVER_HARDWARE |
Ryzen 9 7950X3D (16C/32T), 128GB DDR5, 2x1TB NVMe |
Dashboard hardware info |
SELF_HOSTED_INFRA_JSON |
AX42-U at ax42-01.gograb.space |
Dashboard visibility for the second server |
PLATFORM_GIT_SERVER_URL |
https://ax102-01.gograb.space |
Git repos live on AX102-U since S260 |
PLATFORM_GIT_API_KEY |
AX102-U's key | Matches PLATFORM_GIT_SERVER_URL |
3. Substitution Rules (for doc updates)
NOT every sandbox.gograb.space becomes ax42-01.gograb.space. Apply the correct rule:
Rule 1: AX42-U identity → ax42-01.gograb.space
- SSH:
[email protected]→[email protected] - Health:
https://sandbox.gograb.space/health→https://ax42-01.gograb.space/health - Dashboard:
https://sandbox.gograb.space/sandboxes/...→https://ax42-01.gograb.space/sandboxes/... - DNS label: "sandbox.gograb.space" as server name →
ax42-01.gograb.space - IP mapping: "144.76.69.55 (sandbox.gograb.space)" → "144.76.69.55 (ax42-01.gograb.space)"
- Git clone SSH:
[email protected]:platforms/...→[email protected]:platforms/... - Git clone HTTPS:
https://sandbox.gograb.space/git-http/...→https://ax42-01.gograb.space/git-http/... - scp commands:
[email protected]:/path→[email protected]:/path
Rule 2: Git server → ax102-01.gograb.space
PLATFORM_GIT_SERVER_URL=https://sandbox.gograb.space→https://ax102-01.gograb.space- Docs saying "git operations on sandbox.gograb.space" → note they moved to
ax102-01.gograb.spacein S260
Rule 3: Generic "primary sandbox URL" → ax102-01.gograb.space
SANDBOX_SERVER_URL=https://sandbox.gograb.space→https://ax102-01.gograb.space- Generic "set orchestrator URL to sandbox.gograb.space" →
ax102-01.gograb.space
Rule 4: Historical session reports → annotate, don't rewrite
- "deployed to sandbox.gograb.space" → "deployed to sandbox.gograb.space (now ax42-01.gograb.space)"
4. Work Completed (S282.3)
Runtime files updated (11 files):
.env.local— SANDBOX_SERVERS_JSON, SELF_HOSTED_INFRA_JSON, cleaned up all comments (moved to this doc)APIs/sandbox-orchestrator/src/app/git/git.service.ts— fallback →ax102-01.gograb.spaceAPIs/backend-api/src/app/git-provider/remote-git-provider.service.spec.ts— test URLdeployment/sandbox-orchestrator/deploy-orchestrator-all.sh— SSH fleet targetdeployment/sandbox-orchestrator/deploy-orchestrator-host.sh— default SSH + verify URLsdeployment/sandbox-orchestrator/bootstrap-server.sh— scp source referencesdeployment/sandbox-orchestrator/nginx.conf— installation commenttools/scripts/sandbox/deploy-orchestrator.sh— default servertools/scripts/sandbox/provision-server.sh— default domainapps/gograb-dev/src/app/pages/infrastructure/hetzner-servers/hetzner-deploy-playbook.ts— playbook
Cloudflare DNS — DONE
- Renamed
sandboxA record toax42-01→144.76.69.55(DNS only) sandbox.gograb.spaceno longer exists
Railway — DONE
SANDBOX_SERVERS_JSONupdated withax42-01.gograb.spaceURL for AX42-U entrybackend-ai-apiredeployed
Server-Side (AX42-U) — DONE
- NGINX
server_nameupdated fromsandbox.gograb.spacetoax42-01.gograb.space - SSL cert issued for
ax42-01.gograb.spacevia certbot - Fixed:
sites-enabled/had a stale copy (not a symlink) — replaced with proper symlink tosites-available/ - Both health endpoints verified healthy:
https://ax42-01.gograb.space/health→{"status":"ok"}(30 VM capacity, Firecracker ready)https://ax102-01.gograb.space/health→{"status":"ok"}(60 VM capacity, Firecracker ready)
5. Work Remaining
5A. Documentation (21 files, ~78 occurrences)
Done (S282.5). Sweep applied with substitution rules 1–4; canonical list for reference:
docs/english/sessions/S282/282.5-infrastructure-how-to-resolve-ax42-missing-from-dashboard-and-nam.md
docs/english/sessions/S261/261.5-bugfix-why-were-git-operations-still-routing-to-ax42-u-de.md
docs/english/sessions/S261/261.4-infrastructure-what-are-the-current-hetzner-server-roles-and-rout.md
docs/english/sessions/S260/260.3-architecture-how-should-the-sandboxplacementrouter-be-architect.md
docs/english/sessions/S252/S252-handoff.md
docs/english/sessions/S251/S251-progress-report.md
docs/english/sessions/S251/S251-handoff.md
docs/english/sessions/S245/245-daily-summary-2026-03-20.md
docs/english/Plans/superpowers/specs/archived/2026-03-23-hetzner-dual-server-infrastructure-design.md (12 occurrences)
docs/english/Plans/superpowers/specs/archived/2026-03-20-vitto-personal-whatsapp-channel-design.md (5 occurrences)
docs/english/Plans/superpowers/plans/archived/2026-03-20-platform-infrastructure-evolution.md (11 occurrences)
docs/english/Plans/already-done/february-26-plans/self-hosted-e2b.plan.md
docs/english/Plans/S260: Hetzner Multi-Server Infrastructure and SandboxPlacementRouter.md (8 occurrences)
docs/english/Plans/S259: VSCode Extension Native Git Operations Tab.md
docs/english/Plans/S254-handoff-vscode-clone-support.md (8 occurrences)
docs/english/Plans/S251: Git Operations Library and Hetzner Pipeline Fixes.md
docs/english/Deployment/sandbox-orchestrator-optional-railway-and-dashboard.md
docs/english/Deployment/hetzner-firecracker-operations.md
docs/english/sessions/S152/152-session-report.md
docs/english/sessions/S150/150-handoff-to-session-152.md
docs/english/sessions/S144/144-work-report.md
5B. MongoDB — MCP Decision Records
Canonical script (maintain in repo): tools/scripts/database/update-sandbox-dns-references.mongosh.js — updates sessions.description and sessions.context when context is a string; skips object-shaped context.
Run this script against both MongoDB clusters:
// ── Script: update-sandbox-dns-references.js ──
// Run with: mongosh "mongodb+srv://..." --file update-sandbox-dns-references.js
// ── ai-agents database ──
use('ai-agents');
const textFields = ['chosen', 'reasoning', 'question', 'task'];
const filter = {
$or: textFields.map(f => ({ [f]: { $regex: /sandbox\.gograb\.space/ } }))
};
const count = db.decisions.countDocuments(filter);
print(`Found ${count} decisions with sandbox.gograb.space in ai-agents.decisions`);
if (count > 0) {
const updates = {};
textFields.forEach(f => {
updates[f] = {
$replaceAll: {
input: `$${f}`,
find: 'sandbox.gograb.space',
replacement: 'ax42-01.gograb.space'
}
};
});
const result = db.decisions.updateMany(filter, [{ $set: updates }]);
print(`Updated ${result.modifiedCount} documents in ai-agents.decisions`);
}
// ── ai-mcp-dev database ──
use('ai-mcp-dev');
// core_rules
const crFilter = { content: { $regex: /sandbox\.gograb\.space/ } };
const crCount = db.core_rules.countDocuments(crFilter);
print(`Found ${crCount} core_rules with sandbox.gograb.space`);
if (crCount > 0) {
db.core_rules.updateMany(crFilter, [{
$set: {
content: {
$replaceAll: { input: '$content', find: 'sandbox.gograb.space', replacement: 'ax42-01.gograb.space' }
}
}
}]);
}
// learned_patterns
const lpFilter = {
$or: [
{ pattern: { $regex: /sandbox\.gograb\.space/ } },
{ description: { $regex: /sandbox\.gograb\.space/ } }
]
};
const lpCount = db.learned_patterns.countDocuments(lpFilter);
print(`Found ${lpCount} learned_patterns with sandbox.gograb.space`);
if (lpCount > 0) {
db.learned_patterns.updateMany(lpFilter, [{
$set: {
pattern: {
$replaceAll: { input: '$pattern', find: 'sandbox.gograb.space', replacement: 'ax42-01.gograb.space' }
},
description: {
$replaceAll: { input: '$description', find: 'sandbox.gograb.space', replacement: 'ax42-01.gograb.space' }
}
}
}]);
}
// sessions
const sessFilter = {
$or: [
{ description: { $regex: /sandbox\.gograb\.space/ } },
{ 'context': { $regex: /sandbox\.gograb\.space/ } }
]
};
const sessCount = db.sessions.countDocuments(sessFilter);
print(`Found ${sessCount} sessions with sandbox.gograb.space`);
if (sessCount > 0) {
db.sessions.updateMany(sessFilter, [{
$set: {
description: {
$replaceAll: { input: '$description', find: 'sandbox.gograb.space', replacement: 'ax42-01.gograb.space' }
}
}
}]);
}
print('\n✅ MongoDB DNS rename complete');
6. Verification Checklist
# No remaining references in runtime code
grep -r "sandbox\.gograb\.space" --include="*.ts" --include="*.js" --include="*.sh" \
--include="*.json" --include="*.conf" --exclude-dir=node_modules --exclude-dir=dist \
--exclude-dir=.git --exclude-dir=sessions/manifests
# Expected: 0 results
# No remaining references in docs (after 5A is done)
grep -r "sandbox\.gograb\.space" docs/
# Expected: 0 results
# Both servers healthy
curl -sS https://ax102-01.gograb.space/health # → {"status":"ok"}
curl -sS https://ax42-01.gograb.space/health # → {"status":"ok"}
# Dashboard shows 2 servers
# Navigate to dev.gograb.space/infrastructure/hetzner (or localhost:6300)
# Both AX102-U and AX42-U should be RUNNING
# PlacementRouter fleet status (backend-ai-api logs on startup)
# Should log: "Fleet initialized: ax102-01 (customer-sandbox, git-operations) | ax42-01 (executive-sandbox, customer-sandbox)"
7. Server-Side Lessons Learned
nginx sites-enabled was a copy, not a symlink: On AX42-U, /etc/nginx/sites-enabled/sandbox-orchestrator was a regular file (copied Mar 21), not a symlink to sites-available/. Editing sites-available/ had no effect. Fixed by replacing with a proper symlink. Always verify ls -la /etc/nginx/sites-enabled/ before assuming symlinks.
certbot auto-install can fail with domain rename: certbot --nginx -d ax42-01.gograb.space issued the cert successfully but failed to install it ("Could not automatically find a matching server block"). This happens when certbot can't match the new domain to an existing server block. Fix: update nginx server_name first, then run certbot, or manually set ssl_certificate paths.
8. MCP Decision
Logged as dev_1774700586392_jb5wi96 — type: infrastructure, confidence: 0.95
Generated by Claude Code (Session 282.3) — March 28, 2026