Merge pull request 'feat: sign in from any computer, no private network needed' (#3) from feat/public-link-endpoint into main
This commit is contained in:
@@ -29,8 +29,10 @@ git CLI only — same portability heritage as the estate's `gitea_sync.py` mesh.
|
|||||||
## Quickstart (invited user)
|
## Quickstart (invited user)
|
||||||
|
|
||||||
You need a shre-id account — an operator creates it; there is no open signup
|
You need a shre-id account — an operator creates it; there is no open signup
|
||||||
(see "Invite-only story"). You also need to be on the tailnet: the
|
(see "Invite-only story"). You do **not** need to be on any private network:
|
||||||
provisioning service is not publicly exposed yet.
|
the provisioning service answers at `https://granthi-link.shre.ai`, which is
|
||||||
|
the client's default server. Internal machines can still pass
|
||||||
|
`--server http://100.111.127.127:3042` to reach it over the tailnet.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git clone https://granthi.shre.ai/nirpa/granthi-sync.git
|
git clone https://granthi.shre.ai/nirpa/granthi-sync.git
|
||||||
@@ -166,11 +168,18 @@ So:
|
|||||||
* **Our own machines** may join the tailnet — that is an operator action with
|
* **Our own machines** may join the tailnet — that is an operator action with
|
||||||
an operator's judgement behind it.
|
an operator's judgement behind it.
|
||||||
* **Customer devices never do.** Their transport is public HTTPS to
|
* **Customer devices never do.** Their transport is public HTTPS to
|
||||||
granthi-link and the forge through cloudflared. That is the same exposure
|
granthi-link and the forge through cloudflared. **Done 2026-08-23:**
|
||||||
step already in the promotion window below, and it is what makes a genuinely
|
`https://granthi-link.shre.ai` fronts `:3042` on the `pulse-granthi-edge`
|
||||||
new computer able to onboard itself at all — today `link` only works from
|
tunnel, so a new computer signs itself in with one command and never
|
||||||
inside the tailnet, which means "you can't set up a new computer without an
|
touches the private network.
|
||||||
operator first" is the honest status.
|
|
||||||
|
Two things that bit during that rollout, worth not rediscovering:
|
||||||
|
`link.granthi.shre.ai` fails TLS — Cloudflare's Universal SSL covers
|
||||||
|
`shre.ai` and `*.shre.ai`, **not** a third-level `*.granthi.shre.ai`, so
|
||||||
|
the hostname has to be second-level. And exposure REQUIRES
|
||||||
|
`trust_forwarded_for: true` with `trusted_proxies: ["100.107.37.98/32"]`
|
||||||
|
in the same change: behind the tunnel every request otherwise looks like
|
||||||
|
the tunnel itself, and one abuser would spend everybody's rate budget.
|
||||||
|
|
||||||
## Components
|
## Components
|
||||||
|
|
||||||
@@ -537,8 +546,10 @@ Shape this should take, so the next session does not re-litigate it:
|
|||||||
|
|
||||||
## Promotion window (beta → prod)
|
## Promotion window (beta → prod)
|
||||||
|
|
||||||
1. **Expose :3042** behind cloudflared (granthi.shre.ai vhost or
|
1. ~~**Expose :3042** behind cloudflared~~ **DONE 2026-08-23** —
|
||||||
link.granthi.shre.ai) — today it is tailnet-only by design.
|
`https://granthi-link.shre.ai` (second-level, see above), origin stays
|
||||||
|
tailnet-only, `trust_forwarded_for` on with the tunnel as the only
|
||||||
|
trusted proxy.
|
||||||
2. **Swap forge base URLs** in `/opt/granthi-link/config.json`:
|
2. **Swap forge base URLs** in `/opt/granthi-link/config.json`:
|
||||||
`gitea_base` → prod forge, `public_gitea_base` →
|
`gitea_base` → prod forge, `public_gitea_base` →
|
||||||
`https://granthi.shre.ai`; the client default server URL moves to the
|
`https://granthi.shre.ai`; the client default server URL moves to the
|
||||||
|
|||||||
@@ -87,7 +87,12 @@ ZITADEL_BASE = "https://id.shre.ai"
|
|||||||
DEVICE_CLIENT_ID = "386909715541590022"
|
DEVICE_CLIENT_ID = "386909715541590022"
|
||||||
# ^ Zitadel native app "granthi-sync-device" (appId 386909715541524486) in
|
# ^ Zitadel native app "granthi-sync-device" (appId 386909715541524486) in
|
||||||
# project granthi-forge (386906525790109702); device-code + refresh grants.
|
# project granthi-forge (386906525790109702); device-code + refresh grants.
|
||||||
DEFAULT_SERVER = "http://100.111.127.127:3042"
|
# Public by default: a genuinely new computer cannot be expected to join a
|
||||||
|
# private network before it can sign in. The tailnet address still works and
|
||||||
|
# is what internal machines should pass to --server.
|
||||||
|
DEFAULT_SERVER = os.environ.get("GRANTHI_LINK_SERVER",
|
||||||
|
"https://granthi-link.shre.ai")
|
||||||
|
TAILNET_SERVER = "http://100.111.127.127:3042"
|
||||||
DEVICE_SCOPE = "openid profile email"
|
DEVICE_SCOPE = "openid profile email"
|
||||||
|
|
||||||
FORGE_PAGE_LIMIT = 50
|
FORGE_PAGE_LIMIT = 50
|
||||||
|
|||||||
@@ -1102,5 +1102,23 @@ class TestPruneClockIsPersisted(GitScenarioBase):
|
|||||||
self.assertEqual(pruner.call_count, 0) # no snapshot folders linked
|
self.assertEqual(pruner.call_count, 0) # no snapshot folders linked
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class TestDefaultServer(unittest.TestCase):
|
||||||
|
"""A new computer must be able to sign in without joining a private
|
||||||
|
network first -- that is the whole point of exposing the endpoint."""
|
||||||
|
|
||||||
|
def test_default_server_is_public_https(self):
|
||||||
|
self.assertTrue(client.DEFAULT_SERVER.startswith("https://"),
|
||||||
|
client.DEFAULT_SERVER)
|
||||||
|
self.assertNotIn("100.111.", client.DEFAULT_SERVER)
|
||||||
|
|
||||||
|
def test_env_override_wins_for_internal_machines(self):
|
||||||
|
import importlib
|
||||||
|
with mock.patch.dict(os.environ,
|
||||||
|
{"GRANTHI_LINK_SERVER": "http://10.0.0.5:3042"}):
|
||||||
|
reloaded = importlib.reload(client)
|
||||||
|
self.assertEqual(reloaded.DEFAULT_SERVER, "http://10.0.0.5:3042")
|
||||||
|
importlib.reload(client) # restore for the rest of the suite
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
Reference in New Issue
Block a user