Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions BETA.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <machine>`
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 <code>` 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
Expand All @@ -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.

Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
51 changes: 51 additions & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <machine> 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.
Comment on lines +257 to +259

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Disclose the guest identity visible to the relay

During a guest attach, relayLocator.Dial sends the guest's unencrypted BindingJSON through SignalMsg.Binding; that record contains the stable guest wallet, device ID, and X25519 key. Although the relay does not currently interpret it, it can parse the record and correlate the guest with the owner's shared machine, so this is new observable metadata rather than traffic identical to an owner attach. Document that exposure or encrypt the binding before claiming the relay learns nothing new.

AGENTS.md reference: AGENTS.md:L9-L9

Useful? React with 👍 / 👎.


## Recovery and rotation

- `mir identity export-recovery --yes` intentionally prints a 24-word recovery
Expand Down
13 changes: 12 additions & 1 deletion docs/beta-checklist.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <machine>` (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 <id>`).

**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
Expand Down
1 change: 1 addition & 0 deletions go/internal/cli/share.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Shell-quote the invited machine name

When an inviter supplies a valid machine name containing shell syntax, such as box; evil-command, this copyable instruction executes that syntax on the guest's machine because info.Name is emitted verbatim. agent.ValidMachineName permits spaces and other non-control characters, including shell metacharacters, so quote the name safely for the target shell or print a non-name identifier instead.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Remove the unusable web-app follow-up

After a guest claims the one-time invite with mir join, the machine and grant are saved only under the CLI's --dir; the browser's guest store is populated only when the browser itself claims the invite. Consequently, selecting the web app after this message will not show or authorize the share, and the already-consumed invite cannot be claimed there again. Remove this parenthetical or tell users to open the original invite in the web app instead of using mir join.

Useful? React with 👍 / 👎.

return nil
}
Loading