# @pdsjs/git-host A git forge served from a PDS reached over the wire. The PDS stays the data store and the identity provider: users log in with their atproto account, repositories live in the account's PDS as `dev.pdsjs.git.repo` records with bundle blobs, pushes go to the PDS exactly as the [git helper](../git/README.md) makes them. What moves out is the hosting: this service reads the account's records and blobs through the public XRPC surface and serves the forge from its own origin. ``` git CLI ──────────────────► PDS (uploadBlob + putRecord, branch protection) git-ui ◄──► git-host ─┬─ /git/ smart HTTP, raw files, the file browser ├─ dev.pdsjs.git.* reads, answered from the wire ├─ discovery: Jetstream → SQLite, no Constellation └─ owner login: atproto OAuth, session cookie PDS ── the data store; git hosting there becomes optional ``` ## Running ```sh git-host --pds https://pds.example.com --account alice.example.com \ --db ./forge.sqlite --port 8046 --origin https://git.alice.example.com \ --ui ./git-ui-dist ``` | Option | Purpose | Default | | --- | --- | --- | | `--pds` | the account's PDS base URL | required | | `--account` | the hosted account's handle or DID | required | | `--db` | the index database (SQLite) | `./git-appview.sqlite` | | `--port` | the port to serve on | `8046` | | `--origin` | the origin requests are read as carrying; required for login | host header | | `--ui` | the git-ui build to serve at `/` | none | | `--plc-url` | the directory DIDs resolve against | `https://plc.directory` | | `--jetstream` | the Jetstream to index from (a v2 instance) | `wss://jetstream.us-east.bsky.network` | | `--jetstream-api-key` | the archive's bearer token, for replay on a metered instance; also read from `JETSTREAM_API_KEY` | none | | `--replay` | catch up from the Jetstream archive on boot instead of walking the relay; implies `--no-backfill` | off | | `--list-service` | the relay the backfill lists repos from | `https://bsky.network` | | `--no-watch` | skip the Jetstream tail | | | `--no-backfill` | skip the first-boot historical walk | | ## What it serves - `/git//` — the read-only smart HTTP endpoint and raw files, exactly the surface the embedded extension serves. Stock git clones through it with no helper installed. - `/xrpc/dev.pdsjs.git.*` — the same XRPC methods the embedded extension serves, answered from records and blobs fetched on demand. Record reads are anonymous; the owner's writes carry the owner's OAuth token, which the login flow below put in the session. - `/.well-known/atproto-did` and `/.blobs/` — the two routes the browser app reads from its own origin, so the app works served from the forge without CORS negotiation. - `/` — the [git-ui](../git-ui) build, when `--ui` names one. ## The index Discovery on the PDS asks Constellation, a hosted reverse index, who wrote about a repository. The forge asks its own index instead. Two ways to fill it, one fold: - **The archive, replayed.** Jetstream v2 keeps the whole network in sealed segment files. With `--replay` the forge pages `planSnapshot` for the `dev.pdsjs.git.*` collections, downloads the matching blocks from the archive, folds them into SQLite, and cuts over to the live tail at the sealed tip. A refused live cursor sends the tail back to the archive for the gap. On Bluesky's public v2 instances the archive HTTP is metered and needs `--jetstream-api-key`. - **The relay walk, backfilled.** Without `--replay`, a first boot walks `com.atproto.sync.listReposByCollection` and lists each account's collections from their own PDS, so strangers' pull requests and issues appear without waiting for the next live write. The index is at-least-once by design: a record upserts by uri, a delete removes, and replaying an event already indexed changes nothing. ## Login `GET /login` starts an atproto OAuth authorization-code flow for the forge owner: PKCE, a DPoP-bound token pair, and a session cookie. The client metadata document lives at `/oauth/client-metadata.json`, the callback at `/oauth/callback`. A session whose DID is not the hosted account's grants nothing, because every owner-only procedure compares against the hosted account. The default grant is the `atproto` scope, which covers the record and blob writes the forge makes. Collaborators keep pushing from their own accounts to their own PDSes; the forge reads their copies from there, exactly as the [collaboration model](../../docs/plans/2026-08-21-git-collaboration.md) describes. Repositories held in spaces are not served by the forge yet: the space credential chain needs a consented `space:` grant per member, which the CLI helper holds and the forge does not yet ask for.