diff --git a/BETA.md b/BETA.md index 79080b4..638c0f3 100644 --- a/BETA.md +++ b/BETA.md @@ -24,6 +24,12 @@ This is a beta. Read the gaps below before you rely on it. through the encrypted registry. - The CLI attaches several machines at once (`mir attach a b c`) and switches focus with `Ctrl-O` then a number. +- Share a terminal with someone for a bounded time: `mir share ` + mints an invite (read-only by default, write only by explicit heavy consent, + 1 h default, 24 h cap); the guest joins with `mir join ` or the web + link and the share expires on its own. Read-only guests see one pane and + cannot type; write access is full control, and the prompt says so. See + [SECURITY.md](SECURITY.md#session-sharing). - Machine revocation, native OS-keychain storage for the owner root, signed and reproducible releases, and the shared Go/JavaScript cryptography vectors all carry over from v0.7.0. See [SECURITY.md](SECURITY.md) for the exact @@ -43,6 +49,10 @@ This is a beta. Read the gaps below before you rely on it. - **External audit.** Miranda has not had an independent security audit. A scope document exists ([docs/audit-scope.md](docs/audit-scope.md)), but no audit has been commissioned or completed. +- **Sharing v2 items.** A read-only share mirrors one pane and does not + follow the owner's window switches; revocation reaches the machine only + when it is online (the 24 h cap is the backstop); and shares are minted + from the CLI only — phone minting comes later. - **No telemetry.** Miranda does not phone home, by design. That means we cannot see how the beta is going without your reports — see below. diff --git a/README.md b/README.md index b08573c..9ffa7ba 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,9 @@ Attach several machines at once with `mir attach a b c`; press `Ctrl-O`, then server forwards ciphertext where direct NAT traversal fails. - **Blind discovery.** The relay stores only owner-encrypted machine records while agents are online. +- **Shareable, for a while.** `mir share` hands someone a time-boxed, + read-only view of one terminal — no account, no key exchange, and it revokes + or expires on its own. ## Security in one screen diff --git a/SECURITY.md b/SECURITY.md index 51a6970..14ad367 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -207,6 +207,57 @@ v0.8.0-beta.3: both modes ride the same connection.) Miranda does not hide IP addresses or provide traffic anonymity. Use a separate privacy network if that is a requirement. +## Session sharing + +An owner can share one machine's terminal with a guest for a bounded time +(`mir share`). Design and full threat analysis: +[docs/superpowers/specs/2026-08-30-g1-guest-sharing-design.md](docs/superpowers/specs/2026-08-30-g1-guest-sharing-design.md). + +**The grant.** Sharing is authorized by a grant: an Ed25519 signature by the +owner over a domain-separated canonical record naming one machine, one guest +key, a mode (read-only or read-write), and a validity window (default 1 h, +hard cap 24 h). The agent verifies it against the owner keys it already pins +and re-checks signature, clock, and revocation on **every** attach. The relay +cannot mint, alter, or extend a grant; neither can a guest — guests hold no +owner key, grants are non-transferable, and guests cannot mint sub-grants. +The grant is bound to the guest's key at claim time, so a stolen grant is +useless without the guest's private key; leaking one costs nothing. + +**Read-only (the default)** is a pane mirror, not a tmux client: the agent +streams one pane's output, and guest input is dropped at the agent — there is +no tmux client to escape, so a read-only guest cannot inject a byte, switch +windows, or reach any other session. What it still exposes: **everything that +pane prints while the grant lives**, including any secret the owner displays. +The exfiltration bound is screen content. + +**Read-write is arbitrary code execution as the agent's user** — full tmux +server control and network access as that machine. That is the honest meaning +of sharing a shell, and there is no pretend sandboxing. The mitigations are +consent-side only: `--write` is never the default, the mint prompt says "full +control of as your user" and requires typing the machine name, and +the TTL cap bounds it. A hostile guest machine is equivalent to a hostile +guest: scope, mode, and TTL are the whole containment story. + +**Invites.** A stolen invite code has the same profile as a stolen pairing +code: one claim consumes the room, the owner sees the claimer's identity and +safety number and must approve, and an unclaimed code dies with the room +timeout. Declining costs the attacker the code. The grant itself is not a +session token: every attach runs Noise-KK against the guest's key plus a live +clock and tombstone check, so replayed offers, revoked grants, and expired +grants all fail. + +**Revocation (v1) is agent-local.** `mir share revoke` delivers a tombstone to +the machine over an authenticated session; the agent drops any live guest at +once and refuses future attaches. While the machine is offline the revoke has +not happened — but an offline machine cannot serve the guest either, and the +24 h cap bounds the worst case. Expiry is enforced by the agent's clock; a +machine with a wildly wrong clock mis-enforces TTLs, which `mir doctor`'s +clock-skew check warns about at 5 minutes. + +**The relay learns nothing new:** one opaque pair room and ordinary attach +metadata for the machine — identical to today's traffic. Suppressing traffic +denies service (already true); it never grants access. + ## Recovery and rotation - `mir identity export-recovery --yes` intentionally prints a 24-word recovery diff --git a/docs/beta-checklist.md b/docs/beta-checklist.md index 82f529f..95e3a38 100644 --- a/docs/beta-checklist.md +++ b/docs/beta-checklist.md @@ -55,7 +55,18 @@ you tap it. Then bring the machine back: `mir up` on it, pair fresh. **Note down:** whether the confirmation told you what you needed to know before you tapped; anything surprising on the way back. -## 6. Your setup +## 6. Share a terminal + +On the laptop: `mir share ` (defaults: read-only, 1 h). On the phone +— or a second person's device — open the invite link, read the safety number +aloud, and have the minter approve. Watch the share open read-only, then let +it expire (or `mir share revoke `). + +**Note down:** how long mint → joined took; whether the read-only view showed +live output; whether typing into it did anything (it must not); what happened +at expiry or revoke — the honest end line, or anything confusing. + +## 7. Your setup **Note down:** phone model + browser; laptop browser; passkey provider (iCloud Keychain, 1Password, …); network (home, office, cellular). This feeds diff --git a/go/internal/cli/share.go b/go/internal/cli/share.go index 90f6371..be06bcc 100644 --- a/go/internal/cli/share.go +++ b/go/internal/cli/share.go @@ -360,5 +360,6 @@ func (a *app) cmdJoin(args []string) error { return err } fmt.Fprintf(a.out, "✓ joined %q as a guest — %s access until %s\n", info.Name, modeWord(sg.Mode), time.Unix(sg.NA, 0).Format("15:04")) + fmt.Fprintf(a.out, " open it: %s attach %s (or the web app)\n", a.binary, info.Name) return nil }