pr_01m47d24b0e6n91zwymwxg0vpx/apps/sudo/README.md

103 lines4,994 bytesCodeBlame
1# sudo
2
3g1t's staff console, at <https://sudo.g1t.sh>. It is organised the way
4customers know g1t: by **workspace**.
5
6- **Workspaces** (the home page): every workspace, with its owners, members,
7 who it is billed to, its terms, this month's usage against its limit,
8 what it was charged and what it cost g1t. Search by workspace, owner,
9 email or enterprise; filter to stopped or warning, comped or custom, or
10 on an enterprise. A workspace's page shows its members, and under
11 **Billing** its terms, who it is billed to (move it onto or off an
12 enterprise), a credit form, a Stripe billing link, its ledger and its
13 audit log.
14- **Enterprises**: customers that pay for several workspaces with one
15 bill, one limit and one set of terms. Each has its workspaces (add or
16 remove them), combined usage, terms, credits, ledger and audit log.
17
18Billing's internal account ids (`ws_<slug>` for a workspace's own,
19`ent_…` for an enterprise) are never shown as names; an enterprise's id
20appears only as small "Billing account id" text. Old `/accounts/…` links
21redirect to the workspace or enterprise they meant.
22
23**Cards stay on Stripe.** sudo never shows a card field. To help a customer
24update their card or see invoices, staff make a Stripe billing link on the
25workspace's page (it is recorded) and send it to the owner.
26
27It holds no data. Workspaces, owners and members come from identity's
28staff methods (`admin_workspaces`, `admin_workspace`; `IdentityAdminApi` in
29`packages/contracts/src/identity.ts`); everything about money goes to the
30billing service's (`admin_*`, `BillingAdminApi` in
31`packages/contracts/src/billing.ts`), where each change is recorded with
32the staff member's email. Both are reached over service bindings only, and
33nothing but sudo binds to them.
34
35## How it is locked
36
371. **Cloudflare Access** sits in front of `sudo.g1t.sh` and signs people in.
382. **The worker checks Access's work** on every request, the stylesheet
39 included (`run_worker_first`): it verifies the `Cf-Access-Jwt-Assertion`
40 JWT itself (RS256 against the team's published keys, audience, issuer,
41 expiry), then requires its email to be in `STAFF_EMAILS`. That email is
42 who every change is recorded as. See `app/lib/access.ts`.
433. **It fails closed.** Until `ACCESS_TEAM_DOMAIN`, `ACCESS_AUD` and
44 `STAFF_EMAILS` are all set, every request gets a 403 saying sudo is not
45 configured.
464. **Changes** are POSTs only, and only from sudo's own pages (`Origin`, or
47 `Referer`, must be `https://sudo.g1t.sh`). Terms, enterprise moves, new
48 enterprises and Stripe billing links show a confirmation step first; a
49 credit needs the workspace's slug typed out.
505. **The pages ship no JavaScript.** The content security policy forbids
51 every script and inline style; responses are `no-store`, `noindex` and
52 cannot be framed. The worker has no `workers.dev` address or preview URLs.
53
54## Setting up Access (once, in the Cloudflare dashboard)
55
561. **Zero Trust → Access → Applications → Add an application → Self-hosted.**
57 - Application name: `sudo`.
58 - Session duration: short, such as 8 hours.
59 - Public hostname: `sudo.g1t.sh` (path empty, so it covers everything).
602. **Add a policy** (`g1t staff`): action *Allow*, include *Emails* → the
61 owner's address, and *Emails ending in* → `g1t.sh` for everyone with a
62 g1t address (the same entries as `STAFF_EMAILS`, where a domain is
63 written `@g1t.sh`). Add more staff here *and* in `STAFF_EMAILS`; either
64 one alone is not enough.
653. Save, then open the application's **Overview** (or *Basic information*)
66 and copy the **Application Audience (AUD) tag**.
674. Find the **team domain** under **Zero Trust → Settings → Custom pages**
68 (or *Team name and domain*): it looks like `<team>.cloudflareaccess.com`.
695. Put both into `wrangler.jsonc`:
70
71 ```jsonc
72 "vars": {
73 "ACCESS_TEAM_DOMAIN": "<team>.cloudflareaccess.com",
74 "ACCESS_AUD": "<the AUD tag>",
75 "STAFF_EMAILS": "syntaqx@gmail.com, @g1t.sh"
76 }
77 ```
78
796. Deploy: `scripts/deploy.sh sudo` (after `billing` and `identity`, whose
80 `admin_*` methods it calls).
81
82Visit <https://sudo.g1t.sh>: Access asks you to sign in, then the workspaces
83list opens. Anyone else gets Access's own refusal; anyone Access lets in who
84is not in `STAFF_EMAILS` gets a 403 from the worker.
85
86## Signing in through WARP
87
88Staff signed in to the Zero Trust org in the Cloudflare One agent (WARP)
89reach sudo without the login page: the org allows WARP sessions as Access
90sign-ins (8 hours), the sudo app accepts them, and the `g1t staff` policy
91is also on the WARP enrollment app, so staff can enroll their devices.
92The same two checks still apply: the Access policy, and `STAFF_EMAILS`.
93
94## Working on it
95
96```sh
97npm run typecheck -w @g1t/sudo
98npm test -w @g1t/sudo # JWT verification, forms, money, the workspace join
99npm run build -w @g1t/sudo
100```
101
102`npm run dev` serves the pages, but every request is refused without a real
103Access token, by design.