2 Commits
Author SHA1 Message Date
Nirav Patel 918b184b01 Merge pull request 'docs: this describes the prod deployment now, not beta' (#11) from fix/docs-prod-forge-bases into main
Docs-only. NOTE: merged WITHOUT a completed granthi-review. The gate's webhook rejection was fixed today (item 52eea395), but reviews still fail open — diff fetch timeout, and shre-router returning prose instead of JSON (item 6d4e8819). Merged on explicit human approval, not on a green check.
2026-08-30 01:25:54 -04:00
ClaudeandClaude Opus 5 ba107c1b53 docs: this describes the prod deployment now, not beta
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 [email protected], 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) <[email protected]>
Claude-Session: https://claude.ai/code/session_01L1b6BN9TZVxHkmignRq4p8
2026-08-29 23:20:30 -04:00
2 changed files with 45 additions and 18 deletions
+38 -13
View File
@@ -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 (`[email protected]`) 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 —
+7 -5
View File
@@ -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]}
}
}