Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
ea63c57
feat(portal): choose who may open the public address
rabbitson87 Oct 23, 2026
2ae25a2
feat(access): let in what Portal's own sign-in vouches for
rabbitson87 Oct 23, 2026
682bcae
feat(portal): issue private links from the app
rabbitson87 Oct 23, 2026
ff3d5db
feat(portal): choose who may open the address in the Portal panel
rabbitson87 Oct 23, 2026
167b6fc
docs(portal): who may open the address
rabbitson87 Oct 23, 2026
44e7013
feat(access): let a visitor Portal signed in sign out
rabbitson87 Oct 23, 2026
8f4856c
docs(changelog): Portal's sign-in
rabbitson87 Oct 23, 2026
522c468
fix(access): believe Portal's identity only with the proof it sends
rabbitson87 Oct 23, 2026
43b658e
docs(portal): name v2.7.0 as the first release with sign-in
rabbitson87 Oct 27, 2026
36752d8
fix(portal): show a running login's redirect address, and make the pa…
rabbitson87 Oct 27, 2026
34fd5a2
fix(access): keep a visitor Portal let in from leaving access behind
rabbitson87 Oct 27, 2026
f55f933
fix(access): end Portal's sign-in with the app's when a browser holds…
rabbitson87 Oct 27, 2026
f6470e8
docs(portal): state what a visitor Portal let in may not do, and fix …
rabbitson87 Oct 27, 2026
95e5522
fix(access): sign out of both while herdr is down
rabbitson87 Oct 28, 2026
0f07e02
Merge origin/main into feat/portal-app-auth
devswha Oct 30, 2026
d1cd7a5
fix(access): end a Portal visitor's open streams so an expired link e…
devswha Oct 30, 2026
f9f3169
docs(changelog): a Portal visitor's open page loses access within 5 m…
devswha Oct 30, 2026
8ac86cd
docs(portal): say a visitor who drives has a shell as this PC's user
devswha Oct 30, 2026
2a3ad26
Merge origin/main into feat/portal-app-auth
devswha Oct 30, 2026
2486092
fix(portal): show the short --name host's redirect address too
devswha Oct 30, 2026
a808f43
fix(portal): say why Sign out can look undone with Google or company …
devswha Oct 30, 2026
4a0fbe1
Merge origin/main into feat/portal-app-auth
devswha Oct 31, 2026
2428139
fix(portal): default invited visitors to watch-only access
devswha Oct 31, 2026
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
24 changes: 23 additions & 1 deletion DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -937,7 +937,9 @@ Alert rows of the table above from the `--droplet-*` roles in `src/styles.css`.
action is **Revoke all**; a refused write stays in it as a sentence that says nothing was revoked
and to try again, naming the server's log reference (never a file name). A device that only
watches reads a sentence that says who can do it, with no button (a disabled one would only be a
fainter dead control); an empty list has no row. After a yes the
fainter dead control); an empty list has no row. A browser Portal's own sign-in let in reads the
list under a dim line, **Signed in as {name} with {method}.**, and a hint that pairing and
revoking are for the token or a paired device; it has no Pair, Rename, Revoke or Revoke all. After a yes the
list and the row are gone and one `role="status"` line says every device went and what still
gets in. No keep-this-device choice: the one action the owner needs when a credential is lost
is the one that ends them all. The design follows this app's own paired-device model (the
Expand Down Expand Up @@ -3003,6 +3005,26 @@ Alert rows of the table above from the `--droplet-*` roles in `src/styles.css`.
or a wide Relay row whose text field and Start stack on a phone. Starting and stopping are one
dim status line, at body size (`--fs-md`, `--fs-lg` under a finger) since it is what Portal is
doing now; a failure is the server's sentence over Portal's own lines, folded.
- Who may open the address is the first row of the stopped Portal group: four radios, each a
label with one dim sentence (token or paired device only, chosen at first; private link;
Ethereum wallet; Google or company login). A choice the `portal` on this PC does not list is
disabled, dim, and says why in `.settings-condition`. Under the chosen one: its allow-list as
chips (`--bg-input`, `--radius-pill`, mono) with a text field and Add that check each entry as
Portal does and say what is wrong in `.portal-warning` (`--status-blocked` border,
`--danger-tint`); a chip's remove button keeps its `--chip-h` look and reaches 24px under a
mouse through an invisible expander, and under a finger the chip itself is `--touch-target`
tall (chips one line apart would overlap their expanders). For Google or company login also a
Google/Other choice, the client ID and where the secret is read from, with a warning while it is
not set, and a line saying the redirect address shows once Portal has started. While Portal
runs the same choice is a read-only summary, since changing it means stopping Portal; for Google
or company login it carries the redirect address to register with the provider (copyable, in
`.phone-command`), which only exists once Portal is up. A private-link Portal adds **Invite
someone** (name or a `SettingsSelect` of the listed names, 1, 7, 30 or 90 days, Create link),
which shows the link once in a bearer-secret warning with Copy and Done; its selectable link
keeps the global focus ring and `.input`'s size on a phone. A browser Portal let in has no
Start, Stop or Invite (the server refuses them), as a watching device has none. No coding-agent app offers a public address with a sign-in, so the
design follows an operational one: Cloudflare Access's allow rules (emails and email domains)
and login methods. It differs on purpose: one method at a time, chosen where Portal starts.
- A Portal that was serving and ends on its own is started again once by the server, on the same
relay, port and identity: the address goes, the same dim status line says **Recovering…**
with Stop still offered (disabled on a watch-only device), and a second exit or a restart
Expand Down
14 changes: 10 additions & 4 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Include what you can of:

- the version (Settings, under Updates, opens with `Running vX.Y.Z (commit)`) and, for a remote PC, its OS;
- how the server was reached (localhost, SSH tunnel, `tailscale serve`, LAN, reverse proxy) and how
the client signed in (this PC, Tailscale login, paired device, token);
the client signed in (this PC, Tailscale login, paired device, token, Portal's own sign-in);
- the steps to reproduce, and what an attacker needs beforehand;
- the impact, with evidence from your own setup. Leave out real tokens, pairing codes and cookies.

Expand All @@ -31,8 +31,12 @@ by themselves with `OVIS_AUTO_UPDATE=1`). A fix in the remote-PC runtime ships a

In scope, for example:

- getting in without a matching token, a paired device, the PC's own Tailscale login or a local
connection (`server/access.ts`, `server/auth.ts`), including through a proxy or forged headers;
- getting in without a matching token, a paired device, the PC's own Tailscale login, a local
connection or a visitor Portal's own sign-in vouches for with its proof (`server/access.ts`,
`server/auth.ts`, `server/portal.ts`), including through a proxy or forged headers; and a visitor
Portal let in using the app's own controls to leave access behind, such as pairing a device,
subscribing to push alerts or issuing a link; a watch-only Portal visitor that can type, send
keys, resize, read files or perform a write action is also in scope;
- pairing codes, device cookies and revocation (`server/devices.ts`); a revoked or observe-only
device that can still type, send keys or resize;
- reading files, transcripts or screens outside what the app shows a signed-in client;
Expand All @@ -52,7 +56,9 @@ Out of scope, as documented in [Access and safety](docs/guide.md#access-and-safe
known. It is open to anyone who reaches it, and the server warns about it on startup. Getting past
the gate once a token is set or a device is paired is in scope;
- anything reached through `tailscale funnel`, which is unsupported;
- people you gave access to (a paired device, the token, your tailnet login) using it;
- people you gave drive access to (a driving paired device, the token, your tailnet login, or an
explicitly elevated Portal invitation) using it, including through a terminal: reading the
server's environment or state files, or keeping a way in after their access ends;
- vulnerabilities in herdr, the agents, Tailscale or the browser themselves: report those upstream.

Test only on PCs and accounts you own.
18 changes: 18 additions & 0 deletions changelog.d/portal-sign-in.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
### Added
- **Settings → Phone & devices → Portal** can put Portal's own sign-in in front of the address: a
private link, an Ethereum wallet, or Google or company login (OpenID Connect), each with the
names, wallets or accounts it lets in. Whoever gets through may watch by default, with no access
token asked. Invitations keep a watch role on the server; the owner's app token still drives
and paired devices keep their roles. Devices says who signed in. A visitor let in this way cannot
pair or revoke devices, subscribe to push alerts or control Portal, and a page of theirs left
open loses access within 5 minutes of the link expiring or a sign-out. A private-link Portal
offers **Invite someone**: a link for 1, 7, 30 or 90 days, shown once. The app believes who
Portal signed in only with a proof it hands that Portal in its environment, so other ways to
the app's port cannot pass for it. A sign-in needs Portal v2.7.0 or later, which lists
`--auth-identity-proof` and the OIDC flags; the panel says plainly when the installed `portal`
does not. Google or company login reads its client secret from
`OVIS_PORTAL_OIDC_CLIENT_SECRET`. The default, the access token or a paired device, is
unchanged.
- A browser that Portal's sign-in let in can sign out from **Settings → Devices**. One click ends
both app and Portal sessions when both exist; it does not sign out of the identity provider.
Google or company login shows both callback addresses to register, including the short host.
Loading
Loading