# Join OpenQuick as an agent

OpenQuick turns a local folder containing an `index.html` into a live static site.

## Service state

- Host: https://open-quick-production.up.railway.app
- Status: private preview
- Public access: capability discovery, health, production revision attestation, typed site listing/detail/history reads, and hosted sites
- Write access: operator token or a browser-approved agent deploy credential
- AI chat: authenticated `POST https://open-quick-production.up.railway.app/api/v1/ai/chat` streams guardrailed completions (allowlist, quotas, redacted usage logs). See docs/AI.md.
- Documents: authenticated site-scoped JSON docs at `/api/v1/sites/{slug}/db/{collection}` (SDK: `quick.db.collection`). See docs/DOCUMENTS.md. No client site override.
- Private files: authenticated `/api/v1/sites/{slug}/files` (SDK: `quick.files`). See docs/FILES.md. No client site override.
- Realtime: single-process SSE subscriptions + site channels at `/api/v1/sites/{slug}/realtime` (SDK: `collection.subscribe` / `quick.channel`). See docs/REALTIME.md. Not multi-instance.
- Limits: static files only; maximum decoded release size is 25 MB
- Deploy behavior: one atomic release replaces the selected site slug; rollback reactivates a prior immutable release without copying bytes

## Site URLs

- Mutable current site: `https://open-quick-production.up.railway.app/sites/{slug}/` and nested assets under that prefix. Redeploys update this URL in place.
- Immutable release permalink: `https://open-quick-production.up.railway.app/sites/{slug}/releases/{releaseId}/` and nested assets under that prefix. This always serves the exact bytes of that release.
- Unknown, malformed, cross-site, or traversal release IDs return 404 and never fall back to the current release.
- Ordered release history (metadata only): `GET https://open-quick-production.up.railway.app/api/v1/sites/{slug}/releases`

## MCP (Claude Code / Cursor)

```sh
claude mcp add --transport http openquick https://open-quick-production.up.railway.app/mcp
```

Cursor: add a Streamable HTTP MCP server at `https://open-quick-production.up.railway.app/mcp`. Optional: send `Authorization: Bearer <deploy token>` on MCP HTTP requests (never as a tool argument). Use `openquick_connect` for browser approval; the deploy token stays on the MCP session or bearer header.

## Decide whether you can join

You can deploy only if your runtime can store `OPENQUICK_TOKEN` or `OPENQUICK_TOKEN_FILE` privately (or use the hosted MCP session). Do not ask a human to paste a token into chat. Do not print, log, commit, or place a token in a URL. Send it only in an Authorization header to the exact https://open-quick-production.up.railway.app origin and do not forward it across redirects.

If you do not have a private credential sink, stop after public discovery. The start API fails closed unless privateSink is true.

## Long-lived credential lifecycle

Approval requires the requester-provided six-digit code (the URL alone cannot approve) and creates a handle-bound credential with no expiry; the connection expiresAt applies only while approval is pending. The default pending window is 60 minutes and is operator-configurable with OPENQUICK_APPROVAL_WINDOW_MS (clamped to 24 hours). Start/revive responses include humanMessage, expiresAt, and pollIntervalSeconds; the approval page shows the expiry. If the window elapses before approval, POST https://open-quick-production.up.railway.app/api/v1/agent-connections/{id}/revive with the current clientSecret to keep the same id/handle/approval URL while rotating clientSecret and approvalCode (old secrets fail closed). Optional OPENQUICK_OPERATOR_NOTIFY_WEBHOOK and request notifyCommonsHandle send a secret-free operator notice. An optional string scope in the start request is a literal site-slug prefix. Store the one-time private poll result as OPENQUICK_TOKEN or via OPENQUICK_TOKEN_FILE. Handles are first-come-first-served: revoke before reconnecting the same handle; an active credential may list its handle-only metadata with GET https://open-quick-production.up.railway.app/api/v1/agent-connections and revoke any credential for the same handle with DELETE https://open-quick-production.up.railway.app/api/v1/agent-connections/{id}. Revocation is immediate. Lists expose only id, scope, created_at, last_used_at, and revoked_at—never secret material. Scoped out-of-prefix writes return typed 403 scope_denied.


## First deploy

1. Read [the skill](https://open-quick-production.up.railway.app/skill.md) and [auth rules](https://open-quick-production.up.railway.app/auth.md).
2. Confirm [health](https://open-quick-production.up.railway.app/healthz) returns `{"ok":true}`.
3. Start a browser-mediated connection: POST https://open-quick-production.up.railway.app/api/v1/agent-connections with a proposed handle and privateSink true. Store the JSON privately (mode 0600), including humanMessage, expiresAt, and pollIntervalSeconds (default pending approval window: 60 minutes; OPENQUICK_APPROVAL_WINDOW_MS, max 24h). Ask a human to open approvalUrl and enter the six-digit approvalCode shown in your private start response before expiresAt. If the pending window expires, POST revive with the current clientSecret to keep the same id/handle and receive a new clientSecret plus approvalCode. Poll pollUrl with the current clientSecret until status is approved (honor Retry-After / pollIntervalSeconds on pending), then write token to OPENQUICK_TOKEN or OPENQUICK_TOKEN_FILE. Replay, expiry, and missing privateSink fail closed. Never put the token in a URL or chat.

   Literal poll example (secrets stay in the private sink; never echo them):

   ```sh
   curl -sS -X POST "$POLL_URL"      -H 'content-type: application/json'      -d "{"clientSecret":"$CLIENT_SECRET"}"
   ```
4. Obtain the CLI from the source repository:

   ```sh
   # Preferred after npm publish: npx --yes open-quick@0.1.1
   # Interim: npm install ./open-quick-0.1.1.tgz from a release receipt (npm run pack:cli)
   npx --yes open-quick@0.1.1 init ./my-site
   ```

5. Or select an existing folder containing `index.html`, then deploy with the distributed `openquick` bin (deterministic init; use `--force` only to overwrite):

   ```sh
   export OPENQUICK_HOST=https://open-quick-production.up.railway.app
   # OPENQUICK_TOKEN or OPENQUICK_TOKEN_FILE must already be injected privately. Never echo it.
   openquick deploy ./my-site --json
   ```

6. Verify both returned URLs with GET requests: the mutable `url` and the immutable `releaseUrl`. Report the site slug, public URL, release URL, release ID, file count, and verification timestamp to the operator.

## Good first test

Deploy a disposable slug, verify its HTML and one linked asset, redeploy a changed version to the same slug, and confirm the mutable URL now serves the new release while the first `releaseUrl` still serves the original bytes. Do not overwrite a slug you do not own.

Conformance harness (Space repo): `docs/COLD_ONBOARDING.md` — contract `openquick-cold-onboarding/v1`; CI-safe `npm run harness:cold-onboarding -- --dry-run`.

## Production revision

Credential-free GET https://open-quick-production.up.railway.app/.well-known/openquick-release.json returns the exact Space-main git SHA this process was built to serve (`sourceRevision`), plus `builtAt` and an opaque `deploymentId`. Cache-Control is no-store. Production fails closed unless those values were injected at authorized build/deploy (`OPENQUICK_SOURCE_REVISION`, `OPENQUICK_BUILT_AT`, `OPENQUICK_DEPLOYMENT_ID`). Do not trust request parameters, GitHub main, or hosted files for this pin.

## Machine-readable resources

- https://open-quick-production.up.railway.app/llms.txt
- https://open-quick-production.up.railway.app/skill.md
- https://open-quick-production.up.railway.app/openapi.json
- https://open-quick-production.up.railway.app/.well-known/agent.json
- https://open-quick-production.up.railway.app/.well-known/openquick-release.json
- https://open-quick-production.up.railway.app/mcp
