# GitHub

Lightweight, dependency-free, in-memory GitHub REST v3 + GraphQL API fake for testing code that uses `@octokit/rest`, the `gh` CLI, or the raw GitHub REST/GraphQL API.

Default port: `4767`

## Quick start

Start the server:

```js
import { GithubServer } from "./services/github/src/server.js";

const server = new GithubServer(4767);
await server.start();
// ... run your app/tests ...
await server.stop();
```

Point Octokit at it via `baseUrl`:

```js
import { Octokit } from "@octokit/rest";

const octokit = new Octokit({
  auth: "ghp_parlel",
  baseUrl: "http://127.0.0.1:4767",
});

const { data } = await octokit.rest.users.getAuthenticated();
// data.login => "parlel-user"
```

## Access via MCP / preview URL

Expose the running fake through the parlel pool and address it like the real API:

- REST base URL: `http://127.0.0.1:4767`
- GraphQL endpoint: `http://127.0.0.1:4767/graphql`
- Set `GITHUB_TOKEN=ghp_parlel` and `GITHUB_API_URL=http://127.0.0.1:4767` in your environment.

Any MCP server or agent that reads `GITHUB_API_URL` / `GITHUB_TOKEN` will transparently use the fake.

## Implemented operations

All routes require an `Authorization: Bearer <token>` or `Authorization: token <token>` header (any non-empty token is accepted). State is in-memory and ephemeral.

### REST v3

- `GET /user` — the authenticated user (`id`, `node_id`, `login`, `html_url`, ...).
- `GET /user/repos` — list repositories owned by the authenticated user.
- `POST /user/repos` — create a repository (`201`); rejects missing/duplicate `name` with `422`.
- `GET /repos/:owner/:repo` — retrieve a repository.
- `POST /repos/:owner/:repo` — create-if-absent convenience.
- `PATCH /repos/:owner/:repo` — update description/visibility/default branch.
- `GET /repos/:owner/:repo/issues` — list issues.
- `POST /repos/:owner/:repo/issues` — create issue (`201`); requires `title`.
- `GET /repos/:owner/:repo/issues/:number` — retrieve.
- `PATCH /repos/:owner/:repo/issues/:number` — update title/body/state (open/closed).
- `GET /repos/:owner/:repo/pulls` — list pull requests.
- `POST /repos/:owner/:repo/pulls` — create PR (`201`); requires `title`.
- `GET /repos/:owner/:repo/pulls/:number` — retrieve / `PATCH` update.
- `GET /repos/:owner/:repo/contents/:path` — get file contents (base64).
- `PUT /repos/:owner/:repo/contents/:path` — create/update a file (`201`/`200`) returning `{ content, commit }`.

### GraphQL

- `POST /graphql` — minimal but real handler. `query { viewer { login id name email } }` returns `{ data: { viewer: { login: "parlel-user", ... } } }`. `repository(owner, name) { name nameWithOwner isPrivate description url }` resolves against in-memory repos.

### Service & inspection operations (parlel extensions)

- `GET /` — service metadata.
- `GET /health` — health check (`{ status: "ok" }`).
- `POST /__parlel/reset` — reset all in-memory state.
- `GET /__parlel/repos` — list captured repo keys.
- `OPTIONS *` — CORS preflight (`204`).

## Surface coverage

This emulator faithfully replicates the API surface most application code and agents exercise. Anything below the supported lines is either an intentional design choice for a fast, zero-cost local emulator (✓ By design) or a candidate for a future release (⟳ Roadmap) — never a silent inaccuracy.

Legend: ✅ fully supported · ◐ accepted (stored, not strictly enforced) · ✓ by design · ⟳ on the roadmap.

| Feature | Status |
| --- | --- |
| `GET /user` | ✅ Supported |
| Repos create / get / patch / list | ✅ Supported |
| Issues create / get / list / patch | ✅ Supported |
| Pull requests create / get / list / patch | ✅ Supported |
| Contents get / put (base64) | ✅ Supported |
| GraphQL `viewer` + `repository` | ✅ Supported (subset) |
| Bearer / token auth | ✅ Required (any non-empty token) |
| Full GraphQL schema (mutations, connections, pagination) | ⟳ Roadmap — Only `viewer` + `repository` basics |
| Webhooks / Actions / Checks / Releases | ⟳ Roadmap |
| Real OAuth / scope enforcement | ✓ By design — Any non-empty credential is accepted — no real secrets needed |
| Rate limiting (`403`/`429`) | ✓ By design — Never throttles — local tests run at full speed, zero cost |

## Error codes & shapes

Errors use the GitHub envelope:

```json
{ "message": "Validation Failed", "documentation_url": "https://docs.github.com/rest", "errors": [ ... ] }
```

| Status | When |
| --- | --- |
| `401` | missing/invalid authorization |
| `404` | unknown resource |
| `405` | method not allowed |
| `422` | validation failed (missing `name`/`title`, duplicate repo) |

## Manifest

See `services/github/manifest.json`:

- name: `github`, image: `parlel/github:1`
- port: `4767`, protocol: `http`, healthcheck: `/health`, startup ≈ 100ms
- env: `GITHUB_TOKEN`, `GITHUB_API_URL`
