From ba107c1b53ea7881fbdc8dd9fdeb3b77c26c866a Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 29 Aug 2026 23:20:30 -0400 Subject: [PATCH] docs: this describes the prod deployment now, not beta MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit granthi-link has run against the PROD forge since the promotion, but every document in this repo still described the beta tier. Someone following the README would expect their device to land on granthi-beta.shre.ai; it lands on granthi.shre.ai. Two of the statements were not merely stale but false: "tailnet-only" and "Not publicly exposed" — the service has been public at https://granthi-link.shre.ai since 2026-08-23, and that 404 at / (no root route) has twice been misread as an outage. Verified on root@100.111.127.127, 2026-08-30: gitea_base = http://127.0.0.1:3040 (gitea-central-gitea-1) public_gitea_base = https://granthi.shre.ai systemctl is-active granthi-link -> active https://granthi-link.shre.ai/health -> 200 {"status":"ok","version":"1.1.0"} rate_limit: trust_forwarded_for true, trusted_proxies ["100.107.37.98/32"], no "rules" key -> falls back to DEFAULT_RATE_RULES Docs only. No server, client or test code is touched, and the deployed service is NOT redeployed by this change — it still reports 1.1.0 against a v1.2 repo, which is recorded separately. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01L1b6BN9TZVxHkmignRq4p8 --- README.md | 51 ++++++++++++++++++++++++++++---------- server/config.example.json | 12 +++++---- 2 files changed, 45 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 254ecb6..648a472 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,10 @@ # granthi-sync v1.2 The signup → download → link-folders → cloud product spine for the Granthi -forge, tested against the BETA forge (granthi-beta.shre.ai). Python 3 stdlib + -git CLI only — same portability heritage as the estate's `gitea_sync.py` mesh. +forge. Developed and live-E2E-tested against the BETA forge +(granthi-beta.shre.ai); **the deployed service now runs against the PRODUCTION +forge** — see "Where this actually runs" below. Python 3 stdlib + git CLI only +— same portability heritage as the estate's `gitea_sync.py` mesh. ``` ┌──────────────┐ device flow ┌─────────────────┐ @@ -13,7 +15,8 @@ git CLI only — same portability heritage as the estate's `gitea_sync.py` mesh. │ │ POST /v1/link {zitadel_access_token, device_name} │ │ ───────────────▶ ┌───────────────────────────────┐ │ │ ◀─────────────── │ granthi-link :3042 │ - │ │ {login, token} │ (granthi VPS, tailnet-only) │ + │ │ {login, token} │ (granthi VPS; public via │ + │ │ │ https://granthi-link.shre.ai)│ │ │ │ · userinfo validation │ │ │ POST /v1/repos │ · ensure Gitea user (admin) │ │ │ ───────────────▶ │ · mint scoped user token │ @@ -21,11 +24,28 @@ git CLI only — same portability heritage as the estate's `gitea_sync.py` mesh. │ │ git push/fetch (user token │ admin API │ │ via credential helper) ▼ │ │ ───────────────▶ ┌───────────────────────────────┐ - └──────────────┘ │ BETA forge :3041 │ - │ granthi-beta.shre.ai │ + └──────────────┘ │ PROD forge :3040 │ + │ granthi.shre.ai │ └───────────────────────────────┘ ``` +## Where this actually runs + +Verified on the granthi VPS (`root@100.111.127.127`) on 2026-08-30, because +this file previously described the beta tier long after the deployment moved: + +| | value | +|---|---| +| service | `/opt/granthi-link/`, `granthi-link.service`, `systemctl is-active` → `active` | +| public entry | `https://granthi-link.shre.ai` — `/health` → 200. `/` → 404 is **no root route**, not an outage | +| `gitea_base` | `http://127.0.0.1:3040` (container `gitea-central-gitea-1`) | +| `public_gitea_base` | `https://granthi.shre.ai` | +| rate limiting | `trust_forwarded_for: true`, `trusted_proxies: ["100.107.37.98/32"]`, no `rules` key → falls back to `DEFAULT_RATE_RULES` | +| deployed version | `/health` reports **1.1.0** while this repo is **v1.2** — the running service lags `main` | + +So a new computer that follows Quickstart lands on the **production** forge. +Beta remains where changes are proven before they reach it. + ## Quickstart (invited user) You need a shre-id account — an operator creates it; there is no open signup @@ -194,13 +214,16 @@ So: **64 KB** (413 beyond; missing `Content-Length` → 411, invalid → 400). * `POST /v1/repos {token, name, private}` → creates the user repo with the USER token; clone/html URLs are rebased onto `public_gitea_base` because the - container `ROOT_URL` (https://granthi-beta.shre.ai) does not resolve for - tailnet-only clients. + container `ROOT_URL` does not necessarily resolve for the client that asked + (it was a tailnet-only address on beta; on prod the rebase keeps clone URLs + on `https://granthi.shre.ai` rather than the container's own view). Deployment: `/opt/granthi-link/{granthi_link.py,config.json,state.json}` + systemd unit `granthi-link.service`; binds `127.0.0.1:3042` **and** -`100.111.127.127:3042` (tailnet). **Not publicly exposed** — see promotion -window. The service **refuses to start** (exit 2) unless `config.json` is +`100.111.127.127:3042` (tailnet), and is **publicly reachable** at +`https://granthi-link.shre.ai` through the `pulse-granthi-edge` cloudflared +tunnel (done 2026-08-23; re-verified 2026-08-30). The service **refuses to +start** (exit 2) unless `config.json` is mode 0600/0400 and owned by the user it runs as — the config carries the forge admin password, so permissive perms fail closed, not open. @@ -620,10 +643,12 @@ Shape this should take, so the next session does not re-litigate it: `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`: - `gitea_base` → prod forge, `public_gitea_base` → - `https://granthi.shre.ai`; the client default server URL moves to the - public endpoint. +2. ~~**Swap forge base URLs** in `/opt/granthi-link/config.json`~~ + **DONE — verified live 2026-08-30**: the deployed config reads + `gitea_base: http://127.0.0.1:3040` and + `public_gitea_base: https://granthi.shre.ai`, and the client default + server is already the public endpoint. Linking a new device therefore + creates the account on **prod**. 3. The `granthi-web` OIDC app already lists the prod callback; the device app is host-independent. Rotate the beta admin token/password out of the config when pointing at prod (prod forge is READ-ONLY to this estate — diff --git a/server/config.example.json b/server/config.example.json index 40f1fb6..b53273f 100644 --- a/server/config.example.json +++ b/server/config.example.json @@ -1,15 +1,17 @@ { - "gitea_base": "http://127.0.0.1:3041", - "public_gitea_base": "http://100.111.127.127:3041", + "_comment_forge": "Values below mirror the LIVE prod deployment (verified 2026-08-30). For the beta tier use gitea_base http://127.0.0.1:3041, public_gitea_base https://granthi-beta.shre.ai, container gitea-beta-gitea-1, creds /opt/gitea-beta/.admin-creds.", + "gitea_base": "http://127.0.0.1:3040", + "public_gitea_base": "https://granthi.shre.ai", "zitadel_userinfo": "https://id.shre.ai/oidc/v1/userinfo", - "admin_token": "MINT-VIA: docker exec -u git gitea-beta-gitea-1 gitea admin user generate-access-token --username nirpa --scopes write:admin,write:user,write:repository --raw", + "admin_token": "MINT-VIA: docker exec -u git gitea-central-gitea-1 gitea admin user generate-access-token --username nirpa --scopes write:admin,write:user,write:repository --raw", "admin_login": "nirpa", - "admin_password": "FROM /opt/gitea-beta/.admin-creds (required: Gitea 1.27 token minting only works via basic auth + Sudo header)", + "admin_password": "FROM shre-cred: superadmin/granthi-link/granthi-prod-forge-admin (required: Gitea 1.27 token minting only works via basic auth + Sudo header). NEVER paste this into chat or a shell history.", "binds": [["127.0.0.1", 3042], ["100.111.127.127", 3042]], "test_mode": false, "rate_limit": { "enabled": true, - "trust_forwarded_for": false, + "trust_forwarded_for": true, + "trusted_proxies": ["100.107.37.98/32"], "rules": {"/v1/link": [5, 3600], "/v1/repos": [60, 3600]} } }