M12: principals and roles #12

Open
opened 2026-10-02 00:16:05 +00:00 by jhgaylor · 0 comments
Owner

Depends on: #11
Blocks: #31, #13, #14

From PLAN.md, section "Multiplayer track → M12".


Every request has an author, and every session has an access list.

  • Principals:

    • user: a tailnet login, or an M15 invitee;
    • agent: an M6b block, or a CLI/API token. It acts for a user, with
      at most that user's role;
    • host: an M4 peer daemon.
  • Roles per session:

    • owner: everything, including sharing;
    • editor: create, close and arrange blocks; drive panes; approve
      agents;
    • viewer: watch, scroll, select, copy, capture, tail.

    The daemon's owner is owner of every session.

  • Enforced on every path in one place (a core authorization function
    over intents and API calls): the WebSocket, HTTP API, Unix socket (uid maps
    to the daemon owner), CLI, M5's tmux front end, and federation between
    daemons. M4's per-daemon allowlist becomes this.

  • Grants are data. acl.json per session is written atomically like
    layout.json, and an audit log records grant changes (who, what, when).

  • Push and approvals are per user. Web Push subscriptions belong to a
    principal. needs-input goes to editors who opted in. An agent permission
    request records who approved it.

  • Done when:

    • a second tailnet user with viewer on one session sees it live and
      nothing else;
    • typing, method calls, send and approve from them are refused, with a
      403 on the API and a toast in the UI;
    • granting editor takes effect without reconnecting, and revoking
      disconnects them within a second;
    • the audit log shows each grant and revoke.

Track overview

Superlogical builds sharing in "from the start". illogical adds it as its own
track, for a small group: a few people you'd hand a shell to, plus their
agents. Enterprise access control stays a non-goal (BRIEF.md).

What changes from single-user:

  • config.owner becomes a list of principals with roles.
  • "Last input wins" becomes per-pane driving.
  • Every input byte gets an author.

Order:

  • S12, then M12 and M13 are the core.
  • M14 makes write access safe enough to give out.
  • M15 reaches people outside your tailnet.
  • The track needs M3c (VM tabs) and M4a (federation). It's independent of M6
    to M11, but agent blocks (M6b) gain per-person approvals when both exist.

Decisions this track makes:

Question Decision Why
Unit of sharing The session. Tabs, blocks and machines inherit. Block-level sharing comes later if ever. One grant to reason about; layout stays one shared tree.
Layout One shared tree per session, as today. Focus, scroll, selection and the active tab stay per client. M1's "same layout live everywhere" already is multiplayer layout, like a shared document.
Who types One driver per pane. Viewers take or request control. Free-for-all only in panes marked "pair". Interleaved keystrokes from two people corrupt commands. Superlogical serializes input; we also make it visible.
Pane size Follows the driver. Everyone else letterboxes, as non-owners already do. Extends the existing rule; no new mechanism.
Guests typing on your machine Not by default. A guest's new panes run on a VM (M3b/M3c). Driving one of your local panes needs a per-pane, time-limited "trust" grant. Write access to a local shell is code execution as your uid. VMs make sharing safe by default.
Identity Tailnet identity first (including users from tailnets you share a node with); M15 adds invites for everyone else. Zero new auth for the common case, and it's already proven (S2).
**Depends on:** #11 **Blocks:** #31, #13, #14 _From PLAN.md, section "Multiplayer track → M12"._ --- Every request has an author, and every session has an access list. - **Principals:** - **user:** a tailnet login, or an M15 invitee; - **agent:** an M6b block, or a CLI/API token. It acts *for* a user, with at most that user's role; - **host:** an M4 peer daemon. - **Roles per session:** - **owner:** everything, including sharing; - **editor:** create, close and arrange blocks; drive panes; approve agents; - **viewer:** watch, scroll, select, copy, `capture`, `tail`. The daemon's owner is owner of every session. - **Enforced on every path in one place** (a `core` authorization function over intents and API calls): the WebSocket, HTTP API, Unix socket (uid maps to the daemon owner), CLI, M5's tmux front end, and federation between daemons. M4's per-daemon allowlist becomes this. - **Grants are data.** `acl.json` per session is written atomically like `layout.json`, and an audit log records grant changes (who, what, when). - **Push and approvals are per user.** Web Push subscriptions belong to a principal. `needs-input` goes to editors who opted in. An agent permission request records who approved it. - **Done when:** - a second tailnet user with `viewer` on one session sees it live and nothing else; - typing, method calls, `send` and `approve` from them are refused, with a 403 on the API and a toast in the UI; - granting `editor` takes effect without reconnecting, and revoking disconnects them within a second; - the audit log shows each grant and revoke. --- ### Track overview Superlogical builds sharing in "from the start". illogical adds it as its own track, for a small group: a few people you'd hand a shell to, plus their agents. Enterprise access control stays a non-goal (BRIEF.md). **What changes from single-user:** - `config.owner` becomes a list of principals with roles. - "Last input wins" becomes per-pane driving. - Every input byte gets an author. **Order:** - S12, then M12 and M13 are the core. - M14 makes write access safe enough to give out. - M15 reaches people outside your tailnet. - The track needs M3c (VM tabs) and M4a (federation). It's independent of M6 to M11, but agent blocks (M6b) gain per-person approvals when both exist. **Decisions this track makes:** | Question | Decision | Why | |---|---|---| | Unit of sharing | **The session.** Tabs, blocks and machines inherit. Block-level sharing comes later if ever. | One grant to reason about; layout stays one shared tree. | | Layout | **One shared tree per session, as today.** Focus, scroll, selection and the active tab stay per client. | M1's "same layout live everywhere" already is multiplayer layout, like a shared document. | | Who types | **One driver per pane.** Viewers take or request control. Free-for-all only in panes marked "pair". | Interleaved keystrokes from two people corrupt commands. Superlogical serializes input; we also make it visible. | | Pane size | **Follows the driver.** Everyone else letterboxes, as non-owners already do. | Extends the existing rule; no new mechanism. | | Guests typing on your machine | **Not by default.** A guest's new panes run on a VM (M3b/M3c). Driving one of your local panes needs a per-pane, time-limited "trust" grant. | Write access to a local shell is code execution as your uid. VMs make sharing safe by default. | | Identity | **Tailnet identity first** (including users from tailnets you share a node with); M15 adds invites for everyone else. | Zero new auth for the common case, and it's already proven (S2). |
Sign in to join this conversation.
No description provided.