Member-owned boards and a home page: decisions and order of work #190

Closed
opened 2026-09-18 17:12:50 +00:00 by Grandiras · 3 comments
Owner

Status (2026-09-22): built, not yet released. Every step through #197 is merged on main; #198 is written down and waits on Spaces leaving alpha. None of it is on production: the last release is v2.6.0 (2026-08-27), so the next release carries all of it, #191 included. The two columns the migration rule keeps for one release are dropped in #219, after that release.

Ponente is moving from "admins make the boards and / is the default board" to a shape where every member creates and manages their own boards, with a home page as the way in. Further out, once ATProto Spaces is stable, the data moves into lexicons — a board is roughly a space, its member list the space's members. This issue is the record of what was decided and the order the work goes in; each step is its own issue, linked at the bottom.

It reverses things docs/boards-and-permissions.md and docs/auth.md state as decisions — "a board is not owned", "admins see every board", the default board, LastBoardException, the wordmark's HomeHref, the Admin policy. Those paragraphs get rewritten with the new reason by the step that outdates them, not left to go quietly wrong.

Decided

Roles

Who Defined by May
Super-admin Allowlist:Admins[], read at runtime rather than only seeded manage the invite list; remove any board from its record and ban a member (#196). Sees inside a board only if they are on it like anybody else
Board admin a role on BoardMember everything today's deployment admin can do on that board: rename, columns, tags, access, delete, edit or delete any card or comment
Board member a BoardMember row, or the board being open to everyone what a member can do today
Allowlisted member a Members row sign in, create boards, see the boards shared with them
  • The invite list stays, and is the super-admin's. The Members allowlist — with the access-request queue and the PDS invite codes that feed it — remains the front door, separate from board access, so the app is not open to the world yet. A board admin shares a board with people already on it. Revisited when the app goes ATProto-native.
  • A board a member creates is private to them until they share it. "Everyone on this deployment" stays available as a choice.
  • A restricted board appears in nobody's home page or switcher unless they are on it — the super-admin included.

What / does once it is the home page

You do this You land on
Type the address, open a bookmark or launch the installed app, having last been on a board that board
Click the wordmark from inside the app the home page
Reload on the home page the home page — it is now your last page
Close the tab on the home page, come back tomorrow the home page

The browser says whether a page load began outside the site (Sec-Fetch-Site: none); only those resume. /launch stays as an alias for installs that already have it as their start_url.

Migrating from the old model

Every step that changes the model carries its own migration, to one rule: nobody's access changes on the day it deploys, every address that worked keeps working, and the previous release can still run against the database for one release.

  • Every current deployment admin becomes a board admin on every existing board; existing grants become plain members; an Everyone board stays Everyone (#192, AddBoardRoles).
  • A column the old code reads (Members.Role) is left in place for a release and dropped in a later one — otherwise a rollback has nothing to start on. Left in place; the drop is #219, together with making Boards.Key required (#197 kept it nullable for the same reason).
  • Old URLs redirect: /admin/{columns,tags,access}[/{id}], the bare board-scoped paths that meant "the default board", bookmarks of / (#193, #194). Since #197, a numbered board link also moves on to the board's key.
  • The global board order seeds each member's own, so no list reshuffles (#195).

Decided in #196 (was: parked until the ban and delete design)

Settled on the design posted in #196 and built in #215 and #216:

  • What the super-admin sees of a board they are not on: its record, /admin/board/{key}, reached from the link a report arrives as and never from a list. It shows the name, its admins, how many members, columns, cards and comments it has, and when it was made and last changed. Nothing members wrote besides the name. Each open is logged.
  • Deleting a board that still holds cards: allowed, as a soft delete. The board disappears everywhere at once and can be restored for 30 days (from Recently deleted on the home page, or from its record if the super-admin removed it). Then BoardPurge destroys it, the one place a card is ever destroyed. "Deleting a board never deletes a card" became "nothing is destroyed at the moment of pressing."
  • A board whose last admin is banned: a ban is a flag, not a removal. Their boards pass to the longest-standing member who can still sign in. A board with nobody else on it is left untouched, and never handed to the super-admin, until the ban is lifted.

Order of work

In order — each one needs the one before it, except where it says otherwise:

  • #191 — last page: check the target, server-written cookie (#200). Then a release, so it exists on production. Not released separately: it ships with the rest in the next release.
  • #192 — roles: per-board admins, the super-admin from the config, the migration (#201). Nothing visible changes.
  • #193 — board settings move out of /admin (#202).
  • #194 — the home page at /, members create boards, / resumes from outside (#204).
  • #195 — a board order of your own, and "for you" (#207).

Not scheduled:

  • #196 — super-admin delete and ban: soft delete with a 30-day undo (#215), bans and the board record (#216).
  • #197 — stable keys in board URLs: a TID per board, old numbered links still resolve (#213).
  • #198 — lexicons. The design is written down in docs/lexicons.md (#217). Spaces has existed as an alpha since 2026-08-20 (proposal 0016, Permissioned Data), and #198 stays open until it is stable enough to build against.

Along the way: a QA pass over #191–#195 (#212), a guard so a dev sign-in can never act on a real Atmosphere account (#211), the follow-ups it filed (#205, #209, #210, all closed), and the design canvas brought back in step (#206, #208 → #218).

> **Status (2026-09-22): built, not yet released.** Every step through #197 is merged on `main`; #198 is written down and waits on Spaces leaving alpha. None of it is on production: the last release is v2.6.0 (2026-08-27), so the next release carries all of it, #191 included. The two columns the migration rule keeps for one release are dropped in #219, after that release. Ponente is moving from "admins make the boards and `/` is the default board" to a shape where **every member creates and manages their own boards**, with a **home page** as the way in. Further out, once ATProto Spaces is stable, the data moves into lexicons — a board is roughly a space, its member list the space's members. This issue is the record of what was decided and the order the work goes in; each step is its own issue, linked at the bottom. It reverses things `docs/boards-and-permissions.md` and `docs/auth.md` state as decisions — "a board is not owned", "admins see every board", the default board, `LastBoardException`, the wordmark's `HomeHref`, the `Admin` policy. Those paragraphs get rewritten with the new reason by the step that outdates them, not left to go quietly wrong. ## Decided **Roles** | Who | Defined by | May | |---|---|---| | Super-admin | `Allowlist:Admins[]`, read at runtime rather than only seeded | manage the invite list; remove any board from its record and ban a member (#196). Sees inside a board only if they are on it like anybody else | | Board admin | a role on `BoardMember` | everything today's deployment admin can do *on that board*: rename, columns, tags, access, delete, edit or delete any card or comment | | Board member | a `BoardMember` row, or the board being open to everyone | what a member can do today | | Allowlisted member | a `Members` row | sign in, create boards, see the boards shared with them | - **The invite list stays, and is the super-admin's.** The `Members` allowlist — with the access-request queue and the PDS invite codes that feed it — remains the front door, separate from board access, so the app is not open to the world yet. A board admin shares a board with people already on it. Revisited when the app goes ATProto-native. - **A board a member creates is private to them** until they share it. "Everyone on this deployment" stays available as a choice. - **A restricted board appears in nobody's home page or switcher unless they are on it** — the super-admin included. **What `/` does once it is the home page** | You do this | You land on | |---|---| | Type the address, open a bookmark or launch the installed app, having last been on a board | that board | | Click the wordmark from inside the app | the home page | | Reload on the home page | the home page — it is now your last page | | Close the tab on the home page, come back tomorrow | the home page | The browser says whether a page load began outside the site (`Sec-Fetch-Site: none`); only those resume. `/launch` stays as an alias for installs that already have it as their `start_url`. ## Migrating from the old model Every step that changes the model carries its own migration, to one rule: **nobody's access changes on the day it deploys, every address that worked keeps working, and the previous release can still run against the database for one release.** - [x] Every current deployment admin becomes a board admin on every existing board; existing grants become plain members; an `Everyone` board stays `Everyone` (#192, `AddBoardRoles`). - [ ] A column the old code reads (`Members.Role`) is left in place for a release and dropped in a later one — otherwise a rollback has nothing to start on. **Left in place; the drop is #219**, together with making `Boards.Key` required (#197 kept it nullable for the same reason). - [x] Old URLs redirect: `/admin/{columns,tags,access}[/{id}]`, the bare board-scoped paths that meant "the default board", bookmarks of `/` (#193, #194). Since #197, a numbered board link also moves on to the board's key. - [x] The global board order seeds each member's own, so no list reshuffles (#195). ## Decided in #196 (was: parked until the ban and delete design) Settled on the design posted in #196 and built in #215 and #216: - **What the super-admin sees of a board they are not on**: its *record*, `/admin/board/{key}`, reached from the link a report arrives as and never from a list. It shows the name, its admins, how many members, columns, cards and comments it has, and when it was made and last changed. Nothing members wrote besides the name. Each open is logged. - **Deleting a board that still holds cards**: allowed, as a **soft delete**. The board disappears everywhere at once and can be restored for 30 days (from *Recently deleted* on the home page, or from its record if the super-admin removed it). Then `BoardPurge` destroys it, the one place a card is ever destroyed. "Deleting a board never deletes a card" became "nothing is destroyed at the moment of pressing." - **A board whose last admin is banned**: a ban is a flag, not a removal. Their boards pass to the longest-standing member who can still sign in. A board with nobody else on it is left untouched, and never handed to the super-admin, until the ban is lifted. ## Order of work In order — each one needs the one before it, except where it says otherwise: - [x] #191 — last page: check the target, server-written cookie (#200). ~~Then a release, so it exists on production.~~ Not released separately: it ships with the rest in the next release. - [x] #192 — roles: per-board admins, the super-admin from the config, the migration (#201). Nothing visible changes. - [x] #193 — board settings move out of `/admin` (#202). - [x] #194 — the home page at `/`, members create boards, `/` resumes from outside (#204). - [x] #195 — a board order of your own, and "for you" (#207). Not scheduled: - [x] #196 — super-admin delete and ban: soft delete with a 30-day undo (#215), bans and the board record (#216). - [x] #197 — stable keys in board URLs: a TID per board, old numbered links still resolve (#213). - [ ] #198 — lexicons. The design is written down in `docs/lexicons.md` (#217). Spaces has existed as an alpha since 2026-08-20 (proposal 0016, *Permissioned Data*), and #198 stays open until it is stable enough to build against. Along the way: a QA pass over #191–#195 (#212), a guard so a dev sign-in can never act on a real Atmosphere account (#211), the follow-ups it filed (#205, #209, #210, all closed), and the design canvas brought back in step (#206, #208 → #218).
Collaborator

🤖 On it — the reply will link the full session transcript.

🤖 On it — the reply will link the full session transcript. <!-- opencode-run:task -->
Collaborator

⚠️ The run failed: This operation was aborted


⏱ 45m 2s · 505 steps · $1.8885 · tokens: 2705.5k in / 39.5k out (+48764.2k cache-read) · Go: 5h 23% · wk 34% · mo 19%

📜 Full session transcript

⚠️ The run failed: This operation was aborted --- ⏱ 45m 2s · 505 steps · $1.8885 · tokens: 2705.5k in / 39.5k out (+48764.2k cache-read) · Go: 5h 23% · wk 34% · mo 19% 📜 [Full session transcript](https://git.grandiras.net/Grandiras/opencode-bot/src/branch/opencode-logs/transcripts/Grandiras/Ponente/issue-190/run-2026-09-18T17-36-42-831Z.md) <!-- opencode-session:ses_f4a6870b5ffeWKdw61kFxRgAmf -->
Author
Owner

Brought up to date: every step through #197 is merged (#200–#218, each named in the list above), the parked questions were decided in #196, and #198's design is in docs/lexicons.md. Closing as done. What's left lives elsewhere:

  • The release. Nothing here is on production yet; the next release after v2.6.0 carries all of it.
  • #219: dropping Members.Role and making Boards.Key required, after that release (the migration rule's one-release window).
  • #198: stays open until ATProto Spaces leaves alpha.
Brought up to date: every step through #197 is merged (#200–#218, each named in the list above), the parked questions were decided in #196, and #198's design is in `docs/lexicons.md`. Closing as done. What's left lives elsewhere: - **The release.** Nothing here is on production yet; the next release after v2.6.0 carries all of it. - **#219**: dropping `Members.Role` and making `Boards.Key` required, after that release (the migration rule's one-release window). - **#198**: stays open until ATProto Spaces leaves alpha.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Grandiras/Ponente#190
No description provided.