Files
soleprint/berth/estate/mcrn.json
2026-09-14 03:57:00 -03:00

315 lines
8.8 KiB
JSON

{
"_meta": {
"status": "UNVERIFIED — derived from the repos, not from the estate",
"why": "ppl/infra/ was written and never applied: no ~/.pulumi, no infra/venv, no stack state, files dated 'mar 6'. The estate was built in the console and the IaC is aspirational. B1's inventory is what replaces these values with observed ones; until it runs, every field here is a CLAIM.",
"never_record": "credential values. Resource ids and settings only. nova's gateway secret is deliberately absent from this file even though it is committed in plaintext in ppl/gateway/nginx/conf.d/nova.conf — see services[].raw.",
"sources": [
"ppl/infra/__main__.py",
"ppl/ctrl/dns.sh",
"ppl/ctrl/certs.sh",
"ppl/gateway/docker-compose.yml",
"ppl/gateway/nginx/conf.d/",
"ppl/local/Caddyfile"
],
"placement": {
"box": "a container on the estate's own docker network — upstream is the container name",
"local": "a rig cluster on a peer, reached over the overlay — upstream is that peer's address",
"instance": "a dedicated cloud instance on the overlay — same rendering as `local`",
"hosted": "a managed endpoint. Declared so moving to one is a one-line change; unused.",
"_why": "A service says WHERE it runs. How it is reached follows from that, and the three properties of a static-upstream vhost — upstream{}, no resolver, no set $var — are one decision rather than three."
}
},
"domain": "mcrn.ar",
"local_domain": "local.ar",
"host": "mcrn",
"host_admin": "mcrn-admin",
"instance": {
"type": "t3.small",
"disk_gb": 30,
"disk_type": "gp3",
"image": "debian-12",
"user": "mariano"
},
"firewall": [
{
"port": 22,
"proto": "tcp",
"desc": "SSH"
},
{
"port": 80,
"proto": "tcp",
"desc": "HTTP"
},
{
"port": 443,
"proto": "tcp",
"desc": "HTTPS"
},
{
"port": 3022,
"proto": "tcp",
"desc": "Gitea SSH",
"note": "compose maps 3022:22 but GITEA__server__SSH_PORT=22, so gitea advertises :22 in clone URLs while listening on :3022. B1 confirms which is real."
},
{
"port": 51820,
"proto": "udp",
"desc": "WireGuard",
"note": "ABSENT from ppl/infra/__main__.py's four rules — but the tunnel is live (ping 10.8.0.1 succeeds), so the real security group must already allow it. The code therefore does not describe the estate. Confirm in V1."
}
],
"network": {
"docker_network": "gateway",
"docker_network_note": "A fixed, externally-joinable bridge name. Every unrelated app stack on the box joins it so nginx can resolve them by container name. This is why the gateway compose declares 8 services while nginx routes 20+ hostnames.",
"wireguard_moved": "superseded by the top-level `vpn` block"
},
"vpn": {
"_status": "PARTIAL — addresses and subnet verified from the live wg0 interface on nrft. Peer public keys, endpoints, allowed-ips and keepalive still need `wg show` capture on both ends (V1). Nulls below are unknowns, not defaults.",
"_never_record": "private keys. `wg show` prints 'private key: (hidden)' and is the safe capture command. `wg showconf` dumps PrivateKey= in clear — never use it.",
"overlays": {
"estate": {
"purpose": "Connects the estate's machines across clouds without a shared VPC, and carries everything that does not need to be publicly reachable.",
"subnet": "10.8.0.0/24",
"listen_port": 51820,
"peers": {
"box": {
"address": "10.8.0.1",
"role": "hub",
"note": "mcrn.ar. Has a public IP, so it is the peer others dial. Carries the registry (:5000) and woodpecker's gRPC (:9000), both bound to this address and therefore overlay-only.",
"endpoint": null,
"public_key": null,
"allowed_ips": null
},
"nrft": {
"address": "10.8.0.2",
"role": "roaming",
"note": "The dev box. Behind NAT, so it must initiate and needs PersistentKeepalive. Verified: wg0 UP at 10.8.0.2/24, ping 10.8.0.1 0% loss at 153ms.",
"endpoint": null,
"public_key": null,
"allowed_ips": null,
"keepalive": null
}
}
}
}
},
"databases": [
"gitea",
"woodpecker",
"umami"
],
"certs": {
"issued": [
"mcrn.ar",
"*.mcrn.ar",
"*.spr.mcrn.ar"
],
"issued_source": "ppl/ctrl/certs.sh:92 — the -d flags passed to certbot",
"note": "What the cert ACTUALLY covers. estate_sans() derives what the services NEED. check.sh compares the two; the difference is the finding, not a restatement."
},
"services": [
{
"name": "gitea",
"host": "git",
"upstream": "gitea:3000",
"targets": [
"aws"
]
},
{
"name": "woodpecker",
"host": "ci",
"upstream": "woodpecker-server:8000",
"targets": [
"aws"
]
},
{
"name": "registry",
"host": "registry",
"upstream": "registry:5000",
"targets": [
"aws"
]
},
{
"name": "umami",
"host": "analytics",
"upstream": "umami:3000",
"targets": [
"aws"
]
},
{
"name": "docserve",
"host": "docs",
"upstream": "docserve:8020",
"targets": [
"aws"
]
},
{
"name": "ghost",
"host": "notes",
"upstream": "ghost:2368",
"targets": [
"aws"
]
},
{
"name": "deskmeter",
"host": "deskmeter",
"upstream": "dmweb:10000",
"targets": [
"aws"
],
"local_port": 10000
},
{
"name": "sysmonstm",
"host": "sysmonstm",
"upstream": "sysmonstm-edge:8080",
"targets": [
"aws"
],
"local_port": 8020
},
{
"name": "malvalava",
"host": "malvalava",
"upstream": "mlvclean-frontend:80",
"targets": [
"aws"
],
"local_port": 30090
},
{
"name": "soleprint",
"host": "soleprint",
"upstream": "soleprint:8000",
"targets": [
"aws"
],
"local_port": 12000
},
{
"name": "dlt",
"host": "dlt.spr",
"upstream": "dlt_spr:8000",
"targets": [
"aws"
]
},
{
"name": "sample",
"host": "sample.spr",
"upstream": "sample_spr:8000",
"targets": [
"aws"
]
},
{
"name": "mariano",
"host": "mariano",
"kind": "static",
"targets": [
"aws"
]
},
{
"name": "rigui",
"host": "rig",
"kind": "static",
"targets": [
"aws"
],
"local_port": 20310
},
{
"name": "unt",
"host": "unt",
"targets": [
"local"
],
"local_port": 8040
},
{
"name": "mpr",
"host": "mpr",
"targets": [
"local"
],
"local_port": 30080
},
{
"name": "nvi",
"host": "nvi",
"targets": [
"local"
],
"local_port": 8060
},
{
"name": "eth",
"host": "eth",
"targets": [
"local"
],
"local_port": 8050
},
{
"name": "amar",
"host": "amar",
"targets": [
"local"
],
"local_port": 8030
},
{
"name": "nova",
"host": "nova",
"upstream": "nova-ui:80",
"targets": [
"aws"
],
"raw": true,
"raw_why": "Gated on an X-Gateway-Secret header whose value is committed in plaintext. The value is NOT recorded here. Worse: stellarair.conf proxies to the SAME nova-ui:80 upstream WITHOUT the check, so the gate is bypassable by hostname. Stays hand-written until that is decided."
},
{
"name": "stellarair",
"host": "stellarair",
"upstream": "nova-ui:80",
"targets": [
"aws"
],
"raw": true,
"raw_why": "See nova. Same upstream, no header gate."
},
{
"name": "langfuse",
"host": "langfuse",
"local_host": "lng",
"placement": "local",
"peer": "nrft",
"port": 3000,
"targets": [
"aws",
"local"
],
"local_port": 3000,
"note": "One service, one socket, two names. It was two entries with one flagged `raw`; placement is what made the exception expressible, so it is generated now."
},
{
"name": "legacy",
"host": "*.soleprint",
"upstream": "soleprint:8000",
"targets": [
"aws"
],
"raw": true,
"raw_why": "A regex server_name with a named capture plus sub_filter injection — not expressible as a template. ALSO BROKEN: its /api/, /admin/, /static/ and / blocks proxy to 127.0.0.1, i.e. inside the nginx container where nothing listens, so every legacy room 502s. Only /wrapper/ uses the correct container-name form."
}
]
}