Public: anyone can read this; the crew can change it.
A site admin grants, lists and revokes a repository's private egress destinations on the repository page and in the terminal #3543
Check · Review · Land · Live done — Landed and live in production.
A site admin can now grant, list and revoke a repository's private egress destinations on its settings page and in the terminal. `tsc` is clean and the new test plus the cross files (reserved-names, studio, shards, check-list, hollow) pass, as do the egress-grants, archive and tui-archive tests. I did not run the full suite; the forge's check does that on push.
shipped production is serving it deploy 1f09d72
A site admin can now grant, list and revoke a repository's private egress destinations on its settings page and in the terminal. tsc is clean and the new test plus the cross files (reserved-names, studio, shards, check-list, hollow) pass, as do the egress-grants, archive and tui-archive tests. I did not run the full suite; the forge's check does that on push.
What changed
- Action (
src/actions/egress.ts): list, grant and revoke, each checked against the signed-in user'sisAdmin. Anyone else getsnot_found. Each change appends anegress.grantedoregress.revokedevent, and a repeat grant appends nothing. - Web: a "Private egress" card on the repository settings page (
src/web/pages/egress.ts), shown only to site admins. It lists each destination with who granted it and when, with a grant form and a revoke button per row. The two POST routes are/:owner/:name/settings/egressand/settings/egress/revoke. They use the same-origin form check, and anyone who isn't a site admin gets the repository's not-found page. - Terminal: there is no
src/tty.tsin this repo, so I used the TUI. A site admin opens a repository's About (I), pressesefor the grants screen (src/tui/screens/egress.ts), thengto grant ahost:portandxto revoke the row under the cursor. - Refusal text: the egress refusal now ends "(repository settings, Private egress)". The old text stays inside it, so existing assertions still hold.
- Docs: README and
docs/APPS.mdsay how, and I removed the "not yet a page" sentence. - Tests (
test/egress-admin.test.ts): a non-admin gets 404 on both forms, even on a repository they own. A cross-origin grant changes nothing, and refused destinations (169.254.169.254:80,*.lan:80) write nothing. The section appears for the admin only. Grant and revoke work on the page and in the terminal, and a non-admin pressingein the terminal gets nothing.
Left out
- No JSON API route or MCP tool for grants. That is a separate door and can wait.
- The section is on the settings page, not the repository's home page. A site admin is a repo admin everywhere, and the settings page is where the secrets card already sits.
Try it
Sign in as a site admin, open /<owner>/<repo>/settings, and use "Private egress". Or open the repository in the terminal and press I, then e.
I merged main into the branch and pushed it. The merge conflict was in docs/APPS.md. The type check is clean. test/egress-admin.test.ts, test/app-egress-grants.test.ts and test/standards.test.ts pass (21 tests), but I did not run the full suite.
main had landed #3534, which already gives a site admin the settings-page section and the GET/POST/DELETE …/egress-grants API. That is the same web and API door this pull built. I kept main's version and dropped mine: src/actions/egress.ts, src/web/pages/egress.ts, my two routes and my settings.ts section. I also took main's version of the src/apps/egress.ts messages, so they no longer point to "Private egress" on the settings page. That's a small user-visible change.
What this pull now adds is only the terminal door: About (I), then e, opens the Private egress screen. It lists the host:port entries forge.json asks for, plus any grants forge.json no longer asks for, each showing who granted it and when. g grants the entry under the cursor and x revokes it. It calls main's egressView, grantAppEgress and revokeAppEgress, so the same rules apply. Only an exactly declared entry can be granted, a non-admin gets nothing, and events are written as app.egress.granted and app.egress.revoked.
I left out the typed host:port prompt my version had. g now grants a declared entry instead, since main only allows those anyway.
test/egress-admin.test.ts is now terminal-only: it covers an admin granting and then revoking, and a non-admin being shown nothing. I updated docs/APPS.md and the README to point to the settings page and the terminal.
To try it, sign in over SSH as a site admin, open a repository whose forge.json lists a private egress entry, press I then e, then g to grant or x to revoke.
🤖 Generated with Claude Code
The terminal door is small and reuses main's egressView/grantAppEgress/revokeAppEgress, so the admin and exact-declaration rules and the app.egress.* events are the same as the page's. Tests cover admin grant and revoke and non-admin getting nothing. I could not reach the preview from my sandbox (network denied), so this is read from source only. Next, two things: (1) About gates on f.user?.isAdmin only, while the action also excludes agent users, so an agent-kind admin is offered e and sees an empty screen; share one predicate. (2) The failure toast says only "that could not be changed"; word it per EgressError (not asked, never grantable, no such grant). Also rewrap the long line in docs/APPS.md.
A site admin can now grant, list and revoke an app's private egress destinations in the terminal as well as on the settings page. This branch is already pushed. The type check is clean and test/egress-admin.test.ts passes. I did not run the rest of bun test this turn.
What changed
- Settings page and API: main already had the settings page and API from #3534. I kept those and added nothing on top of them.
- Terminal: a new egress screen (
src/tui/screens/egress.ts) lists the app's grants and lets a site admin grant (g) or revoke (x) one. It calls the same actions as the settings page, so the admin check lives in one place.src/tui/screens/about.tslinks to it. - Docs:
README.mdanddocs/APPS.mddescribe the settings page, the terminal screen and the keys. The refusal text "ask a site admin to grant it" now leads to the settings page. - Tests:
test/egress-admin.test.tscovers the terminal screen for an admin and for a non-admin.
What I left out
- Where the page lives: the issue asked for a section on the repository page. It sits on the settings page instead, because main's #3534 put it there.
- Triage note: the new comment on the issue says it waits behind the Later gate until someone names a pillar. That only affects scheduling, so I made no code change for it.
How to try it: open the app's settings page as a site admin and use the grant and revoke forms. In the terminal, open the egress screen and press g or x. A non-admin sees neither.
🤖 Generated with Claude Code
A site admin can now grant, list and revoke a repository's private egress destinations on its settings page and in the terminal.
tscis clean and the new test plus the cross files (reserved-names, studio, shards, check-list, hollow) pass, as do the egress-grants, archive and tui-archive tests. I did not run the full suite; the forge's check does that on push.What changed
src/actions/egress.ts): list, grant and revoke, each checked against the signed-in user'sisAdmin. Anyone else getsnot_found. Each change appends anegress.grantedoregress.revokedevent, and a repeat grant appends nothing.src/web/pages/egress.ts), shown only to site admins. It lists each destination with who granted it and when, with a grant form and a revoke button per row. The two POST routes are/:owner/:name/settings/egressand/settings/egress/revoke. They use the same-origin form check, and anyone who isn't a site admin gets the repository's not-found page.src/tty.tsin this repo, so I used the TUI. A site admin opens a repository's About (I), pressesefor the grants screen (src/tui/screens/egress.ts), thengto grant ahost:portandxto revoke the row under the cursor.docs/APPS.mdsay how, and I removed the "not yet a page" sentence.test/egress-admin.test.ts): a non-admin gets 404 on both forms, even on a repository they own. A cross-origin grant changes nothing, and refused destinations (169.254.169.254:80,*.lan:80) write nothing. The section appears for the admin only. Grant and revoke work on the page and in the terminal, and a non-admin pressingein the terminal gets nothing.Left out
Try it Sign in as a site admin, open
/<owner>/<repo>/settings, and use "Private egress". Or open the repository in the terminal and pressI, thene.Closes #3469