Public: anyone can read this; the crew can change it.
README is 300 bytes from its 60 KB budget, so every README addition turns its pull red: move Develop and What works today into docs/ (target 45 KB) #6736
Check · Review · Land · Live done — Landed and live in production.
scout/forge:scout/6735-readme-is-300-bytes-from-its-60-kb-budge → main 4 commits fork 2 threads · 2 unresolved
2 files
changed +128
−123
README.md +3 −123
| @@ -358,133 +358,13 @@ work here. | ||
| 358 | 358 | |
| 359 | 359 | ## What works today |
| 360 | 360 | |
| 361 | - **A repository's runs page filters by state**: a row of links (All, then each | |
| 362 | state) above the list, or `/:owner/:name/runs?state=failed` by hand. An unknown | |
| 363 | state is ignored, and the word filter and the pager keep the chosen state. The | |
| 364 | terminal does the same: `H` in a repository opens its runs, and `s` cycles | |
| 365 | All, Failed, Running, Succeeded. | |
| 366 | - **A run page's live log** rides the page's one `/events` connection (its | |
| 367 | `run.log` frames); it opens no stream of its own. Known gaps, both silent: a | |
| 368 | frame the server cuts for size (64 lines or 16 KB) loses its omitted lines | |
| 369 | until the next reconnect's catch-up read, and a run whose log was already cut | |
| 370 | to its last 512 KB at render gets no catch-up read. The page says | |
| 371 | "Reconnecting" while the connection is down. | |
| 372 | ||
| 373 | - **Cut a wave** — Settings → *Cut a wave* (repository admins, same-origin | |
| 374 | POST; web-only, the terminal's settings hand over to the web) opens a pull | |
| 375 | adding `waves/<next>.md`, listing the pulls merged since the last wave and | |
| 376 | not already in an earlier wave. The headline and highlights are `TODO` | |
| 377 | placeholders; no model is called. It refuses, writing nothing, when an | |
| 378 | earlier wave does not parse (naming the file) or nothing new has merged. | |
| 379 | `/:owner/:name/waves` lists the waves on the default branch, newest version | |
| 380 | first, each line opening `/:owner/:name/waves/<version>` (headline, date, | |
| 381 | highlights with why, pulls as links; linked from that settings card); in the | |
| 382 | terminal, `V` in a repository opens the same list and Enter (or a click) on | |
| 383 | a row opens the same wave as plain text (a live browse of another forge says | |
| 384 | waves are only read on this forge). | |
| 385 | `/:owner/:name/waves.atom` is the same list as an Atom feed (newest 50, each | |
| 386 | entry the headline, its highlights and a link to the wave), advertised from | |
| 387 | the waves page; a private repository's feed is a 404 to anyone who cannot | |
| 388 | read it. | |
| 389 | ||
| 390 | - **Accounts & access** — register/sign in, personal access tokens with coarse | |
| 391 | scopes (ro/rw/admin), public and private repositories. | |
| 392 | - **Git** — push and pull over HTTPS **and SSH**, browse code (tree, blob with line anchors, | |
| 393 | commit history) with README rendering and **syntax highlighting**, protected | |
| 394 | default branches (force-push and deletion refused, honest fast-forwards | |
| 395 | allowed). | |
| 396 | - **System memory** — `/system`, for a forge admin who is a person (anyone else gets 403): the forge's RSS beside its JS heap (`heapStats()` from bun:jsc), hourly mean and max for the last 24 h, drawn on the server. Sampled once a minute into `memory_hour` in `app.db` (a week kept). RSS rising while the heap stays flat is native memory growth. In the terminal the same admin has a **System memory** command in the palette: the last 24 hours as text rows (hour, RSS mean/max, heap mean/max in MiB), read-only. The account menu links `/system` for that admin. | |
| 397 | - **Flow alarms** — a firing alarm (red main, a stuck approved pull, an idle crew, no release for 90 min) shows as one linked line above home's feed and on `/boards` (admin), under the vitals line of the terminal's Flow dialog and home, as one phone notification when it starts (not repeated while it stands), and in the `flow` tool's `alarms` (MCP, `/api/v1/telemetry/flow`; an agent's cut to public repositories). Nothing firing draws nothing. | |
| 398 | - **Boards** — `/boards` also shows the pipeline (a row per stage, the bottleneck named) and the held counts (why each autonomous repository's ready issues wait; private repositories included), for the same admin who is a person; they sit as two cards under the board list, not as panels of a board, because they are not saved queries. The terminal's Boards screen does not draw them: its door to the same rows is the **Flow** dialog. `/boards/data`, for a forge admin who is a person (anyone else | |
| 399 | gets a 404 on the page): the query is the URL (`?source=runs&aggregate=count&by=day&fcolumn=state&fvalue=failed`), | |
| 400 | drawn server-side as the chart chosen beside it (`chart=line|bar|area|number|table|heat`) | |
| 401 | with its points in a table; a point opens the rows behind it, keeping your filter. | |
| 402 | `series=<column>` splits each group by a second column: stacked area draws one band per | |
| 403 | value (eight at most, the rest folded into "other"), the heat strip one row per value | |
| 404 | (twelve at most) with time across, the table a series column; line, bar and number set | |
| 405 | it aside. Number is the latest group's value over a sparkline of all of them. | |
| 406 | `/boards` holds the boards themselves: a board is one JSON file | |
| 407 | (`{title, range, refresh: off|10s|1m, panels: [{title, chart, query, w, h}]}`) in | |
| 408 | `forge/boards`, a repo the forge owns and makes on the first boot with an admin; every save is a commit | |
| 409 | by the admin who made it, and a file that does not validate is not shown. Make, rename, | |
| 410 | duplicate, delete, add/edit/remove/reorder panels, or pin the query on `/boards/data`. | |
| 411 | Any panel has **Pin to home** / **Unpin** on its board page (a same-origin POST); pins are per admin, kept in `board_pin` (slug + panel title + position, `boards/pins.ts`), and home draws them under the KPI row in that person's order with Up, Down and Unpin beside each (`web/studio/pinned.ts`). A pin whose panel is gone is skipped, does not count toward the limit of 24, and is dropped at that person's next pin; nobody sees another's pins. All doors go through `actions/board-pins.ts` (an admin who is a person; anyone else gets not-found): | |
| 412 | - the page (Pin to home / Unpin on `/boards/:slug`, Up / Down / Unpin on home); | |
| 413 | - the terminal: on a board's screen `[` `]` mark a panel, `p` pins or unpins it, `<` `>` move a pinned one; the dashboard lists the pinned panels by title; | |
| 414 | - `GET /api/v1/boards/pins` (the list, in order) and `POST /api/v1/boards/pins` with `{"op":"pin|unpin|move","slug":"…","title":"…","by":-1|1}` (`by` for move only); | |
| 415 | - the `board_pins` MCP tool (no `op` lists; `op` pin, unpin or move changes). `pins` is therefore not a board slug. | |
| 416 | The first boot also writes the starting boards, **Usage**, **Serving**, **Crew**, **Releases**, **Apps**, **Security**, **Throughput**, **Reach** and **Forges**; Serving is the forge's event-loop lag p99 beside the checks and crew runs running, so checks' concurrency is raised on numbers. Apps' per-app panels show repo ids until a source exposes a safe name; Security (sign-ins, failed sign-ins, tokens by scope, admin actions, from the admin-only `signins`, `tokens` and `adminActions` sources and `usage`) shows no token text, hash, payload or IP; Throughput is items entering each stage from issue to live, per hour, from the `stages` source. Each is written once: `.offered` in that repo records them, so an edit stays and a deletion sticks. | |
| 417 | A live board (any `refresh` but `off`; the form offers off or on, and `10s` and `1m` are still accepted and mean the same) redraws its grid when a run, pull, check, issue or usage event arrives, through the live-region swap | |
| 418 | (`/boards/:slug/live`, etag'd, one fetch per burst). The same doors are in the API: `GET /api/v1/boards`, `GET|PUT|DELETE /api/v1/boards/:slug`, and each panel's data as JSON or CSV at `GET /api/v1/boards/:slug/panels/:id?from=&to=&step=5m&format=csv`, bucketed in SQL to the step and never cut short (`docs/APPS.md`); each panel's card links `json` and `csv` (a plain download link) beside `explore` | |
| 419 | (an agent or anyone but an admin is 403; a panel whose query cannot run is refused with 422). The same four doors are MCP tools (`boards`, `board_write`, `board_delete`; `boards` with a `slug` reads one, and with a `panel` too that panel's data, with the same `from`, `to` and `step`) and, for the same person, a **Boards** screen in the terminal that lists boards and shows a board's panels as text, each with what its data holds now (a time panel's last bucket and step, another's row count). No dependency was added. | |
| 420 | - **Parked work** — the terminal's Flow dialog says how many issues the crew parked (an open issue whose newest comment is the crew's `Parked by the crew` note) in the last 24 h and 7 d, and names the newest three; the MCP `flow` tool returns the same as `parked`. See `docs/TOUR.md`. | |
| 421 | - **Issues** — labels, threaded comments, close/reopen, markdown. | |
| 422 | - **Pull requests** — open from a branch, a **beautiful diff viewer** (syntax | |
| 423 | highlighting composed with intra-line word highlighting, mobile-first, | |
| 424 | collapsible files), merge-commit and squash strategies, conflict detection, | |
| 425 | and merge commits kept reachable forever via `refs/pull/N/merged`. | |
| 426 | - **Review comments** — anchored to a diff line by content hash, so a | |
| 427 | force-push re-anchors them or marks them outdated, never misplaces them. | |
| 428 | - **Forks** — alternates-backed (instant, storage-bounded), with cross-repo | |
| 429 | pull requests (fork → upstream) and detach-before-delete safety. | |
| 430 | - **Organizations & teams** — orgs own repos; teams grant read/write/admin | |
| 431 | resolved through membership. | |
| 432 | - **Live updates** — a server-sent-event stream turns comment threads live and | |
| 433 | flags new activity, no refresh. | |
| 434 | - **Commit pages** — every commit's diff through the same viewer. | |
| 435 | - **Settings** — visibility, branch protection, and labels. | |
| 436 | - **The check lane is visible.** Checks run one at a time; an admin's home | |
| 437 | strip, the terminal dashboard's strip and `GET /api/v1/health` (`lane`) show | |
| 438 | the queue depth, the running check and its age, the median of the last | |
| 439 | twenty and the time to clear, and the 30-minute queue card names that cause. | |
| 440 | - **A terminal UI** — `ssh` in for the whole forge: dashboard, code browsing | |
| 441 | with syntax highlighting, issues and pulls with live-updating threads, the | |
| 442 | diff viewer with inline review comments, merges, settings, orgs, tokens and | |
| 443 | keys. Six themes, remembered per account. | |
| 444 | - **Origins** — connect GitHub, Forgejo or Gitea (device code, PKCE, or a | |
| 445 | pasted token), mirror repositories in with their issues and pulls, and write | |
| 446 | through to the origin on every comment, state change, label and push. Pins | |
| 447 | say what the local client keeps cloned. | |
| 448 | - **Automation** — a JSON API covering repos, issues, forks, and orgs (the | |
| 449 | label-as-queue surface), a durable event outbox, and **webhooks** (HMAC-signed | |
| 450 | delivery with retry). Every send goes through `src/kernel/outbound.ts`: | |
| 451 | - https URLs only; the host is resolved, private addresses are refused, and no redirect is followed. | |
| 452 | - A refused delivery fails at once, with the reason shown on the hooks page. | |
| 453 | - An unreachable or erroring endpoint is retried with growing delay. | |
| 454 | - **Where an account came from** — `GET /api/v1/admin/accounts` lists each | |
| 455 | account with `via` (`signup`, `edge`, or null) and `signsInThrough` | |
| 456 | (`"open-platform.sh"` for an account the platform edge made, else null); | |
| 457 | Settings → Accounts and the new-account line in the inbox say "signs in | |
| 458 | through open-platform.sh" for the same accounts. The terminal and the MCP | |
| 459 | have no accounts list to carry it. | |
| 460 | - **Removing an account** — `DELETE /api/v1/admin/accounts/:username` (admin | |
| 461 | only). An account that is the last owner of an organization answers 409 with | |
| 462 | the organizations named. Send `{"retireOrgs": true}` to preview: the answer | |
| 463 | lists each organization, its repositories (and the app each runs) and its | |
| 464 | teams, and changes nothing; the account's own repositories and the app each | |
| 465 | runs are listed too, under `own`. Send `{"retireOrgs": true, "confirm": true}` to | |
| 466 | do it: apps are stopped, repositories deleted, organizations retired (the | |
| 467 | name stays taken), then the account. If a repository will not go, the answer | |
| 468 | is 500 with `failed` and `removed`; what is left is intact and the same call | |
| 469 | again finishes the job. The accounts page offers the same flow: Remove → | |
| 470 | "Remove the organization too" → a confirm page listing everything that goes. | |
| 471 | - **Apps** — a repository that runs: production on the default branch, a | |
| 472 | preview per open pull with its own copy of the data, served by hostname | |
| 473 | from the forge's own port, every deploy a run and every run a check. | |
| 474 | - **The crew** — agents as accounts with nothing but a run-scoped token, | |
| 475 | working issues in the background with Claude Code and proposing through | |
| 476 | pulls a person merges. | |
| 477 | ||
| 478 | Pages render without a line of JavaScript and are designed thumb-first. Six | |
| 479 | premade themes (from warm paper to polar night) switch instantly and persist in | |
| 480 | a cookie — even that works script-free. Still **zero runtime dependencies**. | |
| 481 | ||
| 482 | Not yet: containers around apps, custom domains, and a gate that lands a | |
| 483 | pull on its own once review and checks agree. | |
| 361 | The running list of what the forge does, one bullet each, lives in | |
| 362 | [](docs/STATUS.md). | |
| 484 | 363 | |
| 485 | 364 | ## Where the rest is |
| 486 | 365 | |
| 487 | 366 | - [](docs/DEVELOP.md) — developing the forge: tests, checks, the gate, lint and types. |
| 367 | - [](docs/STATUS.md) — what works today, one bullet per feature. | |
| 488 | 368 | This page is the front door. Everything else is a file of its own under |
| 489 | 369 | `docs/`, each about one thing: |
| 490 | 370 |
docs/STATUS.md added +125
| @@ -0,0 +1,125 @@ | ||
| 1 | ## What works today | |
| 2 | ||
| 3 | - **A repository's runs page filters by state**: a row of links (All, then each | |
| 4 | state) above the list, or `/:owner/:name/runs?state=failed` by hand. An unknown | |
| 5 | state is ignored, and the word filter and the pager keep the chosen state. The | |
| 6 | terminal does the same: `H` in a repository opens its runs, and `s` cycles | |
| 7 | All, Failed, Running, Succeeded. | |
| 8 | - **A run page's live log** rides the page's one `/events` connection (its | |
| 9 | `run.log` frames); it opens no stream of its own. Known gaps, both silent: a | |
| 10 | frame the server cuts for size (64 lines or 16 KB) loses its omitted lines | |
| 11 | until the next reconnect's catch-up read, and a run whose log was already cut | |
| 12 | to its last 512 KB at render gets no catch-up read. The page says | |
| 13 | "Reconnecting" while the connection is down. | |
| 14 | ||
| 15 | - **Cut a wave** — Settings → *Cut a wave* (repository admins, same-origin | |
| 16 | POST; web-only, the terminal's settings hand over to the web) opens a pull | |
| 17 | adding `waves/<next>.md`, listing the pulls merged since the last wave and | |
| 18 | not already in an earlier wave. The headline and highlights are `TODO` | |
| 19 | placeholders; no model is called. It refuses, writing nothing, when an | |
| 20 | earlier wave does not parse (naming the file) or nothing new has merged. | |
| 21 | `/:owner/:name/waves` lists the waves on the default branch, newest version | |
| 22 | first, each line opening `/:owner/:name/waves/<version>` (headline, date, | |
| 23 | highlights with why, pulls as links; linked from that settings card); in the | |
| 24 | terminal, `V` in a repository opens the same list and Enter (or a click) on | |
| 25 | a row opens the same wave as plain text (a live browse of another forge says | |
| 26 | waves are only read on this forge). | |
| 27 | `/:owner/:name/waves.atom` is the same list as an Atom feed (newest 50, each | |
| 28 | entry the headline, its highlights and a link to the wave), advertised from | |
| 29 | the waves page; a private repository's feed is a 404 to anyone who cannot | |
| 30 | read it. | |
| 31 | ||
| 32 | - **Accounts & access** — register/sign in, personal access tokens with coarse | |
| 33 | scopes (ro/rw/admin), public and private repositories. | |
| 34 | - **Git** — push and pull over HTTPS **and SSH**, browse code (tree, blob with line anchors, | |
| 35 | commit history) with README rendering and **syntax highlighting**, protected | |
| 36 | default branches (force-push and deletion refused, honest fast-forwards | |
| 37 | allowed). | |
| 38 | - **System memory** — `/system`, for a forge admin who is a person (anyone else gets 403): the forge's RSS beside its JS heap (`heapStats()` from bun:jsc), hourly mean and max for the last 24 h, drawn on the server. Sampled once a minute into `memory_hour` in `app.db` (a week kept). RSS rising while the heap stays flat is native memory growth. In the terminal the same admin has a **System memory** command in the palette: the last 24 hours as text rows (hour, RSS mean/max, heap mean/max in MiB), read-only. The account menu links `/system` for that admin. | |
| 39 | - **Flow alarms** — a firing alarm (red main, a stuck approved pull, an idle crew, no release for 90 min) shows as one linked line above home's feed and on `/boards` (admin), under the vitals line of the terminal's Flow dialog and home, as one phone notification when it starts (not repeated while it stands), and in the `flow` tool's `alarms` (MCP, `/api/v1/telemetry/flow`; an agent's cut to public repositories). Nothing firing draws nothing. | |
| 40 | - **Boards** — `/boards` also shows the pipeline (a row per stage, the bottleneck named) and the held counts (why each autonomous repository's ready issues wait; private repositories included), for the same admin who is a person; they sit as two cards under the board list, not as panels of a board, because they are not saved queries. The terminal's Boards screen does not draw them: its door to the same rows is the **Flow** dialog. `/boards/data`, for a forge admin who is a person (anyone else | |
| 41 | gets a 404 on the page): the query is the URL (`?source=runs&aggregate=count&by=day&fcolumn=state&fvalue=failed`), | |
| 42 | drawn server-side as the chart chosen beside it (`chart=line|bar|area|number|table|heat`) | |
| 43 | with its points in a table; a point opens the rows behind it, keeping your filter. | |
| 44 | `series=<column>` splits each group by a second column: stacked area draws one band per | |
| 45 | value (eight at most, the rest folded into "other"), the heat strip one row per value | |
| 46 | (twelve at most) with time across, the table a series column; line, bar and number set | |
| 47 | it aside. Number is the latest group's value over a sparkline of all of them. | |
| 48 | `/boards` holds the boards themselves: a board is one JSON file | |
| 49 | (`{title, range, refresh: off|10s|1m, panels: [{title, chart, query, w, h}]}`) in | |
| 50 | `forge/boards`, a repo the forge owns and makes on the first boot with an admin; every save is a commit | |
| 51 | by the admin who made it, and a file that does not validate is not shown. Make, rename, | |
| 52 | duplicate, delete, add/edit/remove/reorder panels, or pin the query on `/boards/data`. | |
| 53 | Any panel has **Pin to home** / **Unpin** on its board page (a same-origin POST); pins are per admin, kept in `board_pin` (slug + panel title + position, `boards/pins.ts`), and home draws them under the KPI row in that person's order with Up, Down and Unpin beside each (`web/studio/pinned.ts`). A pin whose panel is gone is skipped, does not count toward the limit of 24, and is dropped at that person's next pin; nobody sees another's pins. All doors go through `actions/board-pins.ts` (an admin who is a person; anyone else gets not-found): | |
| 54 | - the page (Pin to home / Unpin on `/boards/:slug`, Up / Down / Unpin on home); | |
| 55 | - the terminal: on a board's screen `[` `]` mark a panel, `p` pins or unpins it, `<` `>` move a pinned one; the dashboard lists the pinned panels by title; | |
| 56 | - `GET /api/v1/boards/pins` (the list, in order) and `POST /api/v1/boards/pins` with `{"op":"pin|unpin|move","slug":"…","title":"…","by":-1|1}` (`by` for move only); | |
| 57 | - the `board_pins` MCP tool (no `op` lists; `op` pin, unpin or move changes). `pins` is therefore not a board slug. | |
| 58 | The first boot also writes the starting boards, **Usage**, **Serving**, **Crew**, **Releases**, **Apps**, **Security**, **Throughput**, **Reach** and **Forges**; Serving is the forge's event-loop lag p99 beside the checks and crew runs running, so checks' concurrency is raised on numbers. Apps' per-app panels show repo ids until a source exposes a safe name; Security (sign-ins, failed sign-ins, tokens by scope, admin actions, from the admin-only `signins`, `tokens` and `adminActions` sources and `usage`) shows no token text, hash, payload or IP; Throughput is items entering each stage from issue to live, per hour, from the `stages` source. Each is written once: `.offered` in that repo records them, so an edit stays and a deletion sticks. | |
| 59 | A live board (any `refresh` but `off`; the form offers off or on, and `10s` and `1m` are still accepted and mean the same) redraws its grid when a run, pull, check, issue or usage event arrives, through the live-region swap | |
| 60 | (`/boards/:slug/live`, etag'd, one fetch per burst). The same doors are in the API: `GET /api/v1/boards`, `GET|PUT|DELETE /api/v1/boards/:slug`, and each panel's data as JSON or CSV at `GET /api/v1/boards/:slug/panels/:id?from=&to=&step=5m&format=csv`, bucketed in SQL to the step and never cut short (`docs/APPS.md`); each panel's card links `json` and `csv` (a plain download link) beside `explore` | |
| 61 | (an agent or anyone but an admin is 403; a panel whose query cannot run is refused with 422). The same four doors are MCP tools (`boards`, `board_write`, `board_delete`; `boards` with a `slug` reads one, and with a `panel` too that panel's data, with the same `from`, `to` and `step`) and, for the same person, a **Boards** screen in the terminal that lists boards and shows a board's panels as text, each with what its data holds now (a time panel's last bucket and step, another's row count). No dependency was added. | |
| 62 | - **Parked work** — the terminal's Flow dialog says how many issues the crew parked (an open issue whose newest comment is the crew's `Parked by the crew` note) in the last 24 h and 7 d, and names the newest three; the MCP `flow` tool returns the same as `parked`. See `docs/TOUR.md`. | |
| 63 | - **Issues** — labels, threaded comments, close/reopen, markdown. | |
| 64 | - **Pull requests** — open from a branch, a **beautiful diff viewer** (syntax | |
| 65 | highlighting composed with intra-line word highlighting, mobile-first, | |
| 66 | collapsible files), merge-commit and squash strategies, conflict detection, | |
| 67 | and merge commits kept reachable forever via `refs/pull/N/merged`. | |
| 68 | - **Review comments** — anchored to a diff line by content hash, so a | |
| 69 | force-push re-anchors them or marks them outdated, never misplaces them. | |
| 70 | - **Forks** — alternates-backed (instant, storage-bounded), with cross-repo | |
| 71 | pull requests (fork → upstream) and detach-before-delete safety. | |
| 72 | - **Organizations & teams** — orgs own repos; teams grant read/write/admin | |
| 73 | resolved through membership. | |
| 74 | - **Live updates** — a server-sent-event stream turns comment threads live and | |
| 75 | flags new activity, no refresh. | |
| 76 | - **Commit pages** — every commit's diff through the same viewer. | |
| 77 | - **Settings** — visibility, branch protection, and labels. | |
| 78 | - **The check lane is visible.** Checks run one at a time; an admin's home | |
| 79 | strip, the terminal dashboard's strip and `GET /api/v1/health` (`lane`) show | |
| 80 | the queue depth, the running check and its age, the median of the last | |
| 81 | twenty and the time to clear, and the 30-minute queue card names that cause. | |
| 82 | - **A terminal UI** — `ssh` in for the whole forge: dashboard, code browsing | |
| 83 | with syntax highlighting, issues and pulls with live-updating threads, the | |
| 84 | diff viewer with inline review comments, merges, settings, orgs, tokens and | |
| 85 | keys. Six themes, remembered per account. | |
| 86 | - **Origins** — connect GitHub, Forgejo or Gitea (device code, PKCE, or a | |
| 87 | pasted token), mirror repositories in with their issues and pulls, and write | |
| 88 | through to the origin on every comment, state change, label and push. Pins | |
| 89 | say what the local client keeps cloned. | |
| 90 | - **Automation** — a JSON API covering repos, issues, forks, and orgs (the | |
| 91 | label-as-queue surface), a durable event outbox, and **webhooks** (HMAC-signed | |
| 92 | delivery with retry). Every send goes through `src/kernel/outbound.ts`: | |
| 93 | - https URLs only; the host is resolved, private addresses are refused, and no redirect is followed. | |
| 94 | - A refused delivery fails at once, with the reason shown on the hooks page. | |
| 95 | - An unreachable or erroring endpoint is retried with growing delay. | |
| 96 | - **Where an account came from** — `GET /api/v1/admin/accounts` lists each | |
| 97 | account with `via` (`signup`, `edge`, or null) and `signsInThrough` | |
| 98 | (`"open-platform.sh"` for an account the platform edge made, else null); | |
| 99 | Settings → Accounts and the new-account line in the inbox say "signs in | |
| 100 | through open-platform.sh" for the same accounts. The terminal and the MCP | |
| 101 | have no accounts list to carry it. | |
| 102 | - **Removing an account** — `DELETE /api/v1/admin/accounts/:username` (admin | |
| 103 | only). An account that is the last owner of an organization answers 409 with | |
| 104 | the organizations named. Send `{"retireOrgs": true}` to preview: the answer | |
| 105 | lists each organization, its repositories (and the app each runs) and its | |
| 106 | teams, and changes nothing; the account's own repositories and the app each | |
| 107 | runs are listed too, under `own`. Send `{"retireOrgs": true, "confirm": true}` to | |
| 108 | do it: apps are stopped, repositories deleted, organizations retired (the | |
| 109 | name stays taken), then the account. If a repository will not go, the answer | |
| 110 | is 500 with `failed` and `removed`; what is left is intact and the same call | |
| 111 | again finishes the job. The accounts page offers the same flow: Remove → | |
| 112 | "Remove the organization too" → a confirm page listing everything that goes. | |
| 113 | - **Apps** — a repository that runs: production on the default branch, a | |
| 114 | preview per open pull with its own copy of the data, served by hostname | |
| 115 | from the forge's own port, every deploy a run and every run a check. | |
| 116 | - **The crew** — agents as accounts with nothing but a run-scoped token, | |
| 117 | working issues in the background with Claude Code and proposing through | |
| 118 | pulls a person merges. | |
| 119 | ||
| 120 | Pages render without a line of JavaScript and are designed thumb-first. Six | |
| 121 | premade themes (from warm paper to polar night) switch instantly and persist in | |
| 122 | a cookie — even that works script-free. Still **zero runtime dependencies**. | |
| 123 | ||
| 124 | Not yet: containers around apps, custom domains, and a gate that lands a | |
| 125 | pull on its own once review and checks agree. |
new above
offline
Stale: the forge now lands pulls through its gate, so this "Not yet" line misleads. Rewrite it in a follow-up, or drop the gate clause.