Skip to content

feat(host): check project access and refuse peers without it - #523

Merged
iamnbutler merged 4 commits into
mainfrom
access-check
Oct 19, 2026
Merged

iamnbutler merged 4 commits into
mainfrom
access-check

Conversation

@iamnbutler

Copy link
Copy Markdown
Contributor

Fixes #507
Fixes #508
Part of #506. Stacked on #512 (docs).

Data model: the directory gets a shares table through a versioned migration, the first version key it has. The original CREATE TABLE IF NOT EXISTS schema becomes migration 1, so existing directories upgrade in place. GET / now returns shares, and POST /shares adds or removes one.

Access check: apps/host/src/access.ts

  • Direct access:
    • GitHub read access to the project's repository, checked with the deciding host's own gh, or
    • the project is a folder on the person's own host.
  • Through a share: with the team (*) or with the person, by Tailscale login.
  • GitHub check details:
    • A public repository is readable by everyone.
    • The host's own account counts if it can read the repository.
    • Anyone else needs a collaborator permission other than none. GitHub only returns that to accounts with push access (verified against the real API), so a host whose owner only has read access can't confirm teammates. They need a share.
    • Answers are cached for 5 minutes, and a failed check refuses access for 1 minute.
  • Sharing: only people with direct access can share or unshare.

Enforcement on the host that runs the channel: apps/host/src/gateway.ts

  • Each peer's listing, the reply to channels and every broadcast, is filtered for the peer's person. The host's owner on another of their machines still sees everything.
  • Peer channel requests (watch, say, ask, stop, and the rest) and peer project-repo are refused for projects without access.
  • When shares change in the directory, the host resends filtered listings to peers and ends watches that lost access.
  • Peer listings are also resent when a peer's GitHub login arrives late.

New ops:

  • project-access: who can see a project and why, plus whether the owner can share it. Owner only.
  • project-share: owner only. Writes to the directory, then sends shares-changed to every reachable peer, which re-reads the directory. A lobby can't reach someone who was just given access.
  • The UI and CLI for these come in the Share a project with the team or a person #510 PR.

Smoke-tested:

  • wrangler dev directory, upgrading an existing database created by main's code:
    • existing hosts and channels kept;
    • share add, remove and validation;
    • restart with migrations already applied;
    • main's code still reads the migrated database.
  • github.readable against the real API:
    • public repo;
    • private repo as the owner;
    • private repo as a non-collaborator;
    • a repo that doesn't exist.
  • access.ts against that directory, with this host's real gh:
    • own folder vs another host's folder;
    • a share for a person;
    • a team share;
    • refusal to share another person's folder;
    • unshare.
  • A --loopback host boots and lists channels.

Not exercised (needs two people on two Macs): peer listing filtering, refused peer channel requests, and revoking watches when a share is removed. These are in the study scenarios.

Built in Ace

No. Built with Claude Code in a terminal: the session that planned #506 continued straight into implementation, and smoke tests ran wrangler dev and the gh CLI directly. Nothing here needed a capability Ace lacks.

Shares live in the directory's new shares table (versioned migration). apps/host/src/access.ts
grants direct access through GitHub read access or the person's own folder, and access through a
share otherwise. The host running a channel filters each peer's listing by its person and refuses
channel requests and project-repo for projects they can't access. Adds project-access,
project-share, and shares-changed ops; the UI and CLI follow in #510.

Fixes #507
Fixes #508
Why: apps/host/src/gateway.ts#function mayOpen | every peer request that reaches a channel this host runs must pass the project access check; only the host's owner skips it
Why: apps/host/src/access.ts#function share | only people with direct access may change shares, so access from a share is never passed on
Why: apps/host/src/gateway.ts#function peopleChanged | a peer's GitHub login decides what it sees, so learning one late must resend peers' listings
Why: services/directory/src/index.ts#const MIGRATIONS | the first migration predates the version key and must stay idempotent for directories created before it
@iamnbutler
iamnbutler changed the base branch from access-docs to main October 19, 2026 21:09
@iamnbutler
iamnbutler merged commit 2ab6b35 into main Oct 19, 2026
1 of 2 checks passed
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.

Hosts refuse peers without access to a channel's project Store project shares and check project access

1 participant