the-hub-is-reached-from-any-device — The hub is reached privately from any of the owner's devices, and a public ingress is configuration
The hub is a declared deployment target with a tailnet ingress, and the external URL is one typed fact.
BLOCKED 0 of 2 issues done
- ready
- 0
- blocked
- 2
- active
- 0
- verify
- 0
- done
- 0
- cancelled
- 0
Problem
On 2026-09-11 the owner chose Lundra over a free PaaS and asked to try the hub over a Tailscale name now and majordomus.dev (Cloudflare) later. The lundra hub runs as a systemd user unit started by hand; no deployment target describes it and no surface knows its external URL. Proposed in ADR 0204 (The hub is reached privately over the tailnet first; a public ingress is a deployment target, never an application change) (proposed; it carries no authority until the owner accepts it). Sources: chatgpt-majordomus:c118#6, chatgpt-majordomus:c118#9.
Outcome
The hub is a declared deployment target with a tailnet ingress, and the external URL is one typed fact.
Current state
Verified: .ai/repo/deployments/majordomus.yaml declares the GHCR image with listen.interface: all (I0903 and I0904 done) and no hosting provider (owner order 2026-10-12); no external_url or trusted-host field in apps/majordomus-cli/src.
Desired state
Deployment targets local and tailnet share one origin; served observe probes the ingress chain from a tailnet device and from outside it.
Scope
- .ai/repo/deployments
- share/schemas/majordomus/deployment
- apps/majordomus-cli/src/deploy
- apps/majordomus-cli/src/capability/builtin/deploy.rs
Out of scope
- Router port-forwarding
- Any free PaaS
- Joining public CI to the tailnet
Acceptance criteria
- A second authorised device reaches /ui, the API and MCP of the hub over its tailnet name
- No hostname or address of the hub is a source constant
Validation
- majordomus plan validate
Risks
- Tailscale CLI syntax drift (knowledge note)
- Ingress must wait for web-sign-in before anything but reads reaches the hub
Issues
Each one is an execution contract with its own acceptance criteria, validation command and required evidence. The wave column is the layer of the dependency graph it sits in.
| issue | status | wave | depends on | title |
|---|---|---|---|---|
| I3195 | BLOCKED | 2 | I2722, I3196 | The lundra hub is a declared deployment target served over its tailnet name, and a second device reaches it |
| I3196 | BLOCKED | 0 | — | A deployment's external URL, trusted hosts and forwarded headers are one typed fact read by every surface |
The graph
flowchart LR
I3195["I3195<br/>The lundra hub is a declared deployment target served over its tailnet name, and a second device reaches it"]:::blocked
I3196["I3196<br/>A deployment's external URL, trusted hosts and forwarded headers are one typed fact read by every surface"]:::blocked
I3196 --> I3195
classDef done stroke:#16a34a,fill:#dcfce7,color:#14532d,stroke-width:2px
classDef active stroke:#2563eb,fill:#eff6ff,color:#1e3a8a,stroke-width:2px
classDef verify stroke:#7c3aed,fill:#f5f3ff,color:#4c1d95,stroke-width:2px
classDef ready stroke:#0891b2,fill:#ecfeff,color:#164e63,stroke-width:2px
classDef planned stroke:#0891b2,fill:#ecfeff,color:#164e63,stroke-width:2px
classDef blocked stroke:#b45309,fill:#fffbeb,color:#78350f,stroke-width:2px
classDef cancelled stroke:#6b7280,fill:#f9fafb,color:#374151,stroke-width:2px
classDef superseded stroke:#6b7280,fill:#f9fafb,color:#374151,stroke-width:2pxCanonical record: .ai/repo/project/milestones/the-hub-is-reached-from-any-device.yaml. Read it back with majordomus plan show the-hub-is-reached-from-any-device.