Skip to content

Pro app access gate: PIN/password lock on every host - #107

Merged
ryuzdev merged 2 commits into
mainfrom
app-access-gate
Oct 28, 2026
Merged

ryuzdev merged 2 commits into
mainfrom
app-access-gate

Conversation

@ryuzdev

@ryuzdev ryuzdev commented Oct 28, 2026

Copy link
Copy Markdown
Member

Owner request (2026-10-27): Pro owners can set a PIN or password on an app. Platform-enforced in the proxy; not app code.

  • Gate: access_mode open/pin/password + Argon2id hash + version (migration 0021). Owner-only set_access {app, mode, secret?} (62 tools); PIN/password is Pro, opening is always allowed; the gate stays enforced after a downgrade. Console "Visitor access" card, same gates, audited without the secret.
  • Enforcement: before wake, on every host (live, preview, custom domains) and every path/method. Locked HTML navigations get a 401 lock page; everything else 401 plain; X-Robots-Tag: noindex everywhere, gated robots disallows all. POST /.outer/unlock (same-origin required) sets a 30-day __Host-outer_access HMAC cookie (v2.<app_id>.<version>.<expiry>.<host_hash>.<mac>, HKDF from OUTER_SECRET), 303 to a sanitized next.
  • Brute force: PIN 5/IP/15 min + 30/app/24 h; password 10/IP/15 min + 100/app/hour; 8-permit verify semaphore with fail-fast 429; no-store on all refusals.
  • No edge leaks: gated responses are forced private, no-store + Vary: Cookie; set_access purges the app's live, preview and custom-domain cache. App-health path answers synthetic 200 ok without waking or proxying.
  • Bypasses that keep working: look_at_app (single-use host-bound token claimed before the gate), the moderation crawl, deploy health checks, wake/keep-warm (all loopback-direct; no client header trusted).
  • Security review: 2 High (gated Cache-Control, health-path proxy), 3 Medium (spaced cookie strip, CR/LF next, PIN budget + Argon2 bound), 2 Low (host-bound cookie, unlock CSRF), all fixed with failing-before tests. Verified correct: MAC/key separation, version/expiry/app binding, look-token single-use, owner-only authz, no secret in logs/audit, /.outer/ prefix matching, migration + preview copy.

Live on prod (outer:188cf07, throwaway Pro account, deleted after):

  • set_access pin: locked HTML 401 with the form, favicon 401 no-store, health synthetic 200, report reachable, no-Origin/cross-origin 403 with no ledger row, wrong PIN 401, unlock 303 + cookie flags, unlocked asset private, no-store + Vary: Cookie, anon stays 401, live cookie rejected on preview host, robots disallow.
  • Unlock flow in mobile Chromium: wrong PIN message, unlock to the original page, no console errors.
  • Purge: the app's live + preview hosts are purged on every set_access change (host log lines).

fmt, clippy, cargo test (1102) green; bun test pages/console green. One known flake (preview_commit_promote_discard_round_trip fails under parallel load, passes alone; untouched by this change).

Owner-only MCP set_access {app, mode, secret?} (Pro; open always
allowed), enforced by the app proxy before any wake or forward:
locked navigations get a 401 Outer-served page (app name, one
input, Unlock, strict CSP), everything else 401 plain text.
Unlock is POST /.outer/unlock (same-origin checked, rate-limited
10/IP/app/15min and 100/app/hour through rate_limit_events,
Argon2id verify off the async runtime), setting a 30-day
__Host-outer-access cookie (HMAC, app+version+expiry bound) that
the proxy strips before forwarding. Look-token renders, the
celld health gate, loopback crawls/wakes and the disallowing
robots answer bypass the gate; a forged header never does.
Console Settings gains a Visitor access card (Pro-gated, audited
without the secret); get_app and console detail show
access open|pin|password, never the hash. Downgrade leaves a set
gate enforced. Tool count 61 -> 62; tools.md regenerated.
@coderabbitai

coderabbitai Bot commented Oct 28, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 2 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: b38fd74f-c7e3-4fc1-80db-ffbc2b7a2b05
Commits

Reviewing files that changed from the base of the PR and between f940d11 and 188cf07.

Files ignored due to path filters: 1
  • Cargo.lock is excluded by !**/*.lock
Files selected for processing: 29
  • crates/outer/Cargo.toml
  • crates/outer/migrations/0021_app_access_gate.sql
  • crates/outer/src/access.rs
  • crates/outer/src/alerts.rs
  • crates/outer/src/apps/mod.rs
  • crates/outer/src/billing/downgrade.rs
  • crates/outer/src/billing/mod.rs
  • crates/outer/src/deploy/pipeline.rs
  • crates/outer/src/http/console.rs
  • crates/outer/src/http/console_actions.rs
  • crates/outer/src/http/proxy.rs
  • crates/outer/src/lib.rs
  • crates/outer/src/main.rs
  • crates/outer/src/mcp/mod.rs
  • crates/outer/src/mcp/registry.rs
  • crates/outer/src/mcp/tests.rs
  • crates/outer/src/mcp/tools/access.rs
  • crates/outer/src/mcp/tools/apps.rs
  • crates/outer/src/mcp/tools/mod.rs
  • crates/outer/src/moderation/crawl.rs
  • crates/outer/src/preview.rs
  • web/docs/account/plans.mdx
  • web/docs/agents/start-here/quickstart.md
  • web/docs/collaboration/sharing.mdx
  • web/docs/reference/tools.md
  • web/pages/console/_islands/actions/settings.ts
  • web/pages/console/_islands/api.ts
  • web/pages/console/_islands/views.tsx
  • web/pages/console/_islands/webmcp.test.ts
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@ryuzdev
ryuzdev merged commit 7a0f2c6 into main Oct 28, 2026
5 checks passed
@ryuzdev
ryuzdev deleted the app-access-gate branch October 28, 2026 12:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant