Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
d35592b
merge dev into main for the v2.32.1 release
lidge-jun Aug 25, 2026
71c57ea
release: v2.32.1
lidge-jun Aug 25, 2026
d560ac6
merge dev into main for the v2.33.0 release
lidge-jun Aug 25, 2026
08ada6f
Merge pull request #2553 from lidge-jun/codex/promote-main-2330
lidge-jun Aug 25, 2026
ec51e42
release: v2.33.0
lidge-jun Aug 25, 2026
e25b653
merge dev into main for the v2.34.0 release
lidge-jun Aug 27, 2026
80fff9a
Merge pull request #2760 from lidge-jun/codex/promote-main-2340
lidge-jun Aug 27, 2026
fc4de77
Merge pull request #2826 from lidge-jun/codex/promote-main-2350
lidge-jun Aug 28, 2026
c7d8407
Merge pull request #3002 from lidge-jun/codex/promote-main-2360
lidge-jun Aug 30, 2026
54e2274
Merge pull request #3037 from lidge-jun/codex/promote-main-2370
lidge-jun Aug 31, 2026
2c4dca1
merge dev into the promotion branch for v2.38.0
lidge-jun Aug 31, 2026
a34e8b7
merge dev into the promotion branch for v2.38.0 (picks up the ReDoS fix)
lidge-jun Aug 31, 2026
ebb4d55
Merge pull request #3073 from lidge-jun/codex/promote-main-2380
lidge-jun Aug 31, 2026
682112e
Merge remote-tracking branch 'origin/dev' into codex/promote-main-2390
lidge-jun Sep 1, 2026
af6113a
merge dev into main for the v2.39.0 release
lidge-jun Sep 1, 2026
847f4f1
merge dev into main for the v2.40.0 release
Sep 2, 2026
ac78647
Merge pull request #3261 from lidge-jun/codex/promote-main-2400
lidge-jun Sep 2, 2026
aaa9eaf
fix(release): pass the bump job's permissions through the reusable-wo…
lidge-jun Sep 2, 2026
35ff3a4
Merge pull request #3263 from lidge-jun/codex/promote-main-2400-relfix
lidge-jun Sep 2, 2026
bae0674
docs(security): harden generic management proxies
luvs01 Sep 3, 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
8 changes: 8 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@ jobs:
bump-dev-version:
needs: publish
if: ${{ inputs.dry-run != true }}
# A reusable-workflow CALL cannot grant the callee more than the calling job holds,
# and GitHub refuses the whole run at startup when the called workflow's own job
# declares permissions the caller did not pass down ("startup_failure", runs
# 33615174183 / 33615177849 — the first dispatches since #3129 wired this call).
# The callee's job declares exactly these two; nothing else in this file gains them.
permissions:
contents: write
pull-requests: write
uses: ./.github/workflows/dev-version-bump.yml
with:
released-version: v${{ inputs.version }}
Expand Down
23 changes: 17 additions & 6 deletions docs-site/src/content/docs/guides/remote-hub.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ ocx config set hostname 100.64.0.10
ocx config set hub.managementPublicOrigin '"https://hub-name.tailnet-name.ts.net"'
ocx config set corsAllowOrigins '["http://localhost:10100"]'
ocx config set hub.managementIngress '{"enabled":true,"port":10101}'
ocx config set remoteGui.allowedTailscaleUsers '["operator@example.com"]'
ocx config set remoteGui.allowedTailscaleUsers '[]'

# Generate/read this in a protected operator shell or secret manager.
# It is a data-admission token, not a provider credential.
Expand Down Expand Up @@ -115,7 +115,14 @@ tailscale serve status

Set `hub.managementPublicOrigin` to the exact HTTPS origin shown by Serve. Add the operator's exact
Tailscale login to `remoteGui.allowedTailscaleUsers`; an empty list means no remote identity can mint
a session. Verify both directions:
a session. Enable that allowlist only when Tailscale Serve is the process forwarding to the
management ingress:

```bash
ocx config set remoteGui.allowedTailscaleUsers '["operator@example.com"]'

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 Restart the hub after enabling the allowlist

In the documented systemd/launchd sequence, ocx service install starts the hub while this setting is still empty, and this later ocx config set only persists config.json; startServer captures the configuration once at startup in src/server/index.ts:645, with no live reload for remoteGui. Consequently the allowed Tailscale user still cannot mint a session and the positive dashboard step fails until the service is restarted. Add ocx service restart after this command, or establish Serve and set the allowlist before installing the service.

Useful? React with 👍 / 👎.

```

Verify both directions:

```bash
# Negative: the loopback-only port must not be reachable through the node's tailnet address.
Expand All @@ -138,8 +145,10 @@ tailscale cert hub-name.tailnet-name.ts.net
```

Protect the private key, renew it through Tailscale's supported mechanism, and proxy only to
`127.0.0.1:10101`. A generic TLS proxy does not supply trustworthy Tailscale identity. Do not
fabricate `Tailscale-User-*` headers; use the single-use, origin-bound pairing flow instead.
`127.0.0.1:10101`. A generic TLS proxy does not supply trustworthy Tailscale identity. Keep
`remoteGui.allowedTailscaleUsers` empty, and configure the proxy to remove or reject every incoming
`Tailscale-User-*` header before forwarding. Preserving client-supplied identity headers is unsafe;
do not merely avoid adding new ones. Use the single-use, origin-bound pairing flow instead.
Comment on lines +148 to +151

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 Apply the spoofing fix to localized guides

The French, Japanese, Korean, Russian, Turkish, Simplified Chinese, and Traditional Chinese guides/remote-hub.md pages still set remoteGui.allowedTailscaleUsers before Tailscale Serve is established and tell generic-proxy operators only not to fabricate identity headers, without requiring removal of client-supplied headers. Users following any localized guide therefore remain exposed to the header-spoofing path this change is intended to close; update every directly affected translation with the empty-by-default allowlist and header-stripping requirement.

AGENTS.md reference: docs-site/AGENTS.md:L7-L10

Useful? React with 👍 / 👎.


## Headless OAuth

Expand Down Expand Up @@ -231,14 +240,16 @@ docker compose run --rm hub bun run src/cli/index.ts config set runtimeRole hub
docker compose run --rm hub bun run src/cli/index.ts config set hostname 0.0.0.0
docker compose run --rm hub bun run src/cli/index.ts config set hub.managementPublicOrigin '"https://hub-name.tailnet-name.ts.net"'
docker compose run --rm hub bun run src/cli/index.ts config set hub.managementIngress '{"enabled":true,"port":10101}'
docker compose run --rm hub bun run src/cli/index.ts config set remoteGui.allowedTailscaleUsers '["operator@example.com"]'
docker compose run --rm hub bun run src/cli/index.ts config set remoteGui.allowedTailscaleUsers '[]'
docker compose up -d
```

Do not put a token in `ARG`, `ENV`, `COPY`, Compose YAML, image history, or the command line. Do not
mount the Docker socket, host home, Codex home, SSH agent, or provider-key files. Publish only port
`10100`. A management ingress bound to `127.0.0.1:10101` inside the container is reachable only by a
TLS/tailnet frontend in the same network namespace; never publish `10101` as a shortcut.
TLS/tailnet frontend in the same network namespace; never publish `10101` as a shortcut. Keep the
allowlist empty and use pairing for a generic frontend. If a Tailscale Serve sidecar is the process
forwarding to `10101`, set the exact operator allowlist only after that topology is in place.

After the container is healthy, run a separate readiness promotion check:

Expand Down
7 changes: 4 additions & 3 deletions structure/05_gui-and-management-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,10 @@ Tailscale identity headers authorize session issuance only when the request arri
listener and the exact login appears in `remoteGui.allowedTailscaleUsers`. The public listener and
the unauthenticated data-loopback listener always pass `trustedTailscaleIngress: false`, regardless
of `Host`, `Origin`, `Forwarded`, `X-Forwarded-*`, or `Tailscale-User-*` values. A generic TLS proxy
cannot establish that identity and uses the existing single-use, digest-only, origin-bound pairing
exchange. Pairing accepts no admin/data credential substitute and consumes a grant only after the
full origin predicate succeeds.
cannot establish that identity: its deployment must keep the Tailscale allowlist empty, remove or
reject every client-supplied `Tailscale-User-*` header, and use the existing single-use, digest-only,
origin-bound pairing exchange. Pairing accepts no admin/data credential substitute and consumes a
grant only after the full origin predicate succeeds.

The server issues a local in-memory session for five minutes or a remote session for twelve hours,
with 128 live sessions maximum. Every session is bound to the exact server and browser origins;
Expand Down
Loading