papercliped 1.1.0-beta.2 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/.claude-plugin/marketplace.json +14 -11
  2. package/.claude-plugin/plugin.json +17 -8
  3. package/.mcp.json +8 -14
  4. package/CHANGELOG.md +70 -1
  5. package/LICENSE +21 -21
  6. package/README.md +80 -80
  7. package/commands/paperclip-report.md +5 -5
  8. package/commands/paperclip-status.md +4 -4
  9. package/commands/paperclip-sync.md +5 -5
  10. package/dist/accounts/alias.js +1 -1
  11. package/dist/accounts/username.js +1 -1
  12. package/dist/cli.js +11 -11
  13. package/dist/http.js +17 -3
  14. package/dist/manage/routes.js +60 -82
  15. package/dist/oauth/pages.js +32 -18
  16. package/dist/oauth/paperclip-login.js +38 -0
  17. package/dist/oauth/pg-store.js +0 -13
  18. package/dist/oauth/provider.js +83 -64
  19. package/dist/oauth/scopes.js +19 -16
  20. package/dist/openapi.js +19 -2
  21. package/dist/public-api/health.js +46 -0
  22. package/dist/public-api/openapi.js +51 -0
  23. package/dist/public-api/routes.js +251 -0
  24. package/dist/public-api/types.js +14 -0
  25. package/dist/server.js +4 -0
  26. package/dist/site/landing.js +3 -3
  27. package/dist/site/markdown.js +2 -1
  28. package/dist/site/site.js +16 -4
  29. package/dist/telemetry/recorder.js +1 -1
  30. package/dist/tools.js +48 -2
  31. package/dist/web/csp.js +16 -0
  32. package/dist/web/hosts.js +23 -0
  33. package/dist/web/routes.js +132 -0
  34. package/docs/ARCHITECTURE.md +74 -73
  35. package/docs/COMMUNITY.md +86 -0
  36. package/docs/DEPLOY-RENDER.md +3 -3
  37. package/docs/DOMAINS.md +74 -0
  38. package/docs/LAUNCH.md +50 -34
  39. package/docs/RELEASING.md +37 -0
  40. package/docs/SECURITY.md +82 -65
  41. package/docs/SITEMAP.md +92 -0
  42. package/docs/V3-SUBDOMAINS.md +106 -0
  43. package/docs/chatgpt.md +20 -20
  44. package/docs/least-privilege.sql +23 -23
  45. package/docs/legal/PRIVACY.md +37 -37
  46. package/docs/legal/TERMS.md +11 -11
  47. package/docs/oauth.md +61 -62
  48. package/docs/supabase-schema.sql +8 -1
  49. package/migrations/001_init.sql +84 -84
  50. package/migrations/002_audit.sql +41 -41
  51. package/migrations/003_accounts.sql +66 -66
  52. package/migrations/004_privacy_panel.sql +57 -57
  53. package/migrations/006_two_levels.sql +5 -0
  54. package/package.json +10 -14
  55. package/site/docs/chatgpt.md +1 -1
  56. package/site/docs/cli.md +27 -0
  57. package/site/docs/connect-your-paperclip.md +120 -0
  58. package/site/docs/environment.md +64 -0
  59. package/site/docs/faq.md +31 -0
  60. package/site/docs/getting-started.md +11 -5
  61. package/site/docs/limits-and-errors.md +32 -0
  62. package/site/docs/manage-api.md +33 -0
  63. package/site/docs/manage.md +12 -21
  64. package/site/docs/other-ai-apps.md +28 -0
  65. package/site/docs/paperclip-plugin.md +36 -0
  66. package/site/docs/permissions.md +5 -5
  67. package/site/docs/public-api.md +62 -0
  68. package/site/docs/security.md +2 -2
  69. package/site/docs/self-hosting.md +43 -0
  70. package/site/docs/signup.md +3 -3
  71. package/site/docs/tools.md +47 -0
  72. package/site/docs/troubleshooting.md +2 -2
  73. package/site/docs/what-is-papercliped.md +37 -0
  74. package/site/privacy.md +5 -3
  75. package/site/terms.md +1 -1
  76. package/skills/paperclip/SKILL.md +23 -23
  77. package/dist/demo.js +0 -144
  78. package/dist/manage/page.js +0 -170
  79. package/dist/panel/config.js +0 -61
  80. package/dist/panel/main.js +0 -90
  81. package/dist/panel/page.js +0 -553
  82. package/dist/panel/routes.js +0 -284
  83. package/dist/panel/server.js +0 -37
  84. package/docs/panel-role.sql +0 -26
@@ -1,73 +1,74 @@
1
- # Architecture, auth and distribution plan
2
-
3
- Status: research + recommendation (Oct 2026). Sources: Paperclip source/docs (`paperclipai/paperclip`, read at depth‑1 clone) and Anthropic's connector/plugin docs. Items marked **[unverified]** are inferences or come from secondary sources and need testing before we rely on them.
4
-
5
- ## 1. Verdict: integrate, don't rebuild
6
-
7
- Keep Paperclip as the system of record and build *around* it. Rebuilding is not justified:
8
-
9
- - Paperclip is a large, active codebase (8k+ files, ~295 DB migrations, adapters for ~10 agent runtimes, governance, budgets, approvals, MCP gateway, plugin SDK). Our goals — control, sync, reports, API use — are all reachable through its existing REST API. Nothing we need requires different core semantics.
10
- - A fork forfeits upstream fixes and the Paperclip community's agents/templates (ClipHub). A rebuild would cost far more than the integration gaps below.
11
- - The real gaps are **auth scoping** and **transport/OAuth**, and both can be solved in a front layer or contributed upstream.
12
-
13
- ## 2. What Paperclip gives us (relevant facts)
14
-
15
- | Area | Fact | Consequence |
16
- | --- | --- | --- |
17
- | API | REST under `/api`; company-scoped; JSON; errors `{error}` | Our bridge is a thin client — no DB access, no business logic |
18
- | Auth modes | `local_trusted` (no login, loopback) · `authenticated/private` · `authenticated/public` (Better Auth sessions) | Bridge must work with no token (local) and with a token (everything else) |
19
- | Operator creds | **Board API keys** (`board_api_keys`: hashed, `expiresAt`, `revokedAt`, `lastUsedAt`, bound to a *user*). **No scope or company column** | A board key = that user's full board power. Never hand one to a public third party |
20
- | Login for tools | CLI challenge flow: client creates a challenge → user approves at `approvalUrl` in Paperclip's UI → client polls and receives a board token | Reusable as our "Sign in with Paperclip" step (no passwords touch our code) |
21
- | Agent creds | Agent keys / run JWTs; pause, resume, approve etc. are **board‑only** | Operator control must use board creds (documented in README) |
22
- | OAuth | Paperclip is an OAuth **client** (to upstream MCP servers). It is **not** an authorization server | claude.ai / ChatGPT connectors can't authenticate to Paperclip directly |
23
- | MCP | Official `@paperclipai/mcp-server` is a stdio wrapper for use inside agent runs. "Gateway mode" is agent→upstream, with profiles/policies/audit. No operator‑facing remote `/mcp` | Our bridge is the missing operator-side endpoint |
24
- | Plugins | Real plugin runtime: manifest + worker + UI; capability‑gated worker APIs; `apiRoutes` mounted only at `/api/plugins/:id/api/*`; plugins are trusted code; capability additions → `upgrade_pending` until an operator approves; install by npm/local path/image catalog (`paperclipai plugin install`). **No public plugin marketplace** (ClipHub = company templates only) | A native plugin is possible but distribution is operator‑installed, not store‑based |
25
-
26
- ## 3. How public plugins actually ship (Claude & ChatGPT)
27
-
28
- **Claude** (docs.claude.com connector + plugin docs):
29
- - **Plugin bundle** (skills, commands, local MCP server like ours): public GitHub repo with `.claude-plugin/plugin.json`, README, LICENSE → `claude plugin validate` → submit at `claude.ai/directory/manage` ("Plugin bundle") → automated validation + security scan, sometimes human review → publish; later versions follow the tracked branch/tag. Data‑handling questions at submit time. Org can also share privately via a git marketplace (`/plugin marketplace add owner/repo`) — works today.
30
- - **Remote MCP connector** (what claude.ai web/mobile/Desktop use): must be HTTPS. Auth = OAuth 2.0 (DCR or Client‑ID‑Metadata‑Document, PKCE S256, `401` + `WWW-Authenticate: Bearer resource_metadata=…`, RFC 9728 protected‑resource metadata, RFC 8414 AS metadata, form‑encoded `/token`, refresh rotation, 10 s endpoint latency), or none, or a **static header credential (beta, limited orgs)**. Callbacks: `https://claude.ai/api/mcp/auth_callback` and a port‑agnostic loopback for Claude Code. Every tool needs `title` + `readOnlyHint`/`destructiveHint` (we already set these). Reviewers need a populated test account. Custom (unlisted) connectors can be added by URL by any user/org without review.
31
- - Plugins that reference a remote server should submit that server as a connector too.
32
-
33
- **ChatGPT** **[unverified: from secondary sources; I could not fetch OpenAI's docs]**:
34
- - *Apps SDK / MCP apps*: streamable‑HTTP MCP + OAuth 2.1 (DCR, PKCE); public submission needs a verified OpenAI org, public ToS + privacy policy, support contact, demo video, test account and test cases.
35
- - *Custom GPT Actions*: OpenAPI + API‑key or OAuth; shareable via link or GPT Store. This is what we ship today (bearer key).
36
-
37
- ## 4. Auth design for public use
38
-
39
- Problem: board keys are all‑powerful, and the connector UIs want OAuth. Solution: put an **OAuth 2.1 authorization server inside the bridge** that fronts Paperclip.
40
-
41
- ```
42
- claude.ai / ChatGPT ──OAuth 2.1 + PKCE──▶ bridge (AS + resource server) ──board token──▶ Paperclip /api
43
- │
44
- /authorize ──▶ create Paperclip CLI-auth challenge ──▶ user approves in Paperclip UI
45
- (bridge receives board token, stores it encrypted, mints its OWN scoped token)
46
- ```
47
-
48
- - **Sign‑in:** reuse Paperclip's challenge/approve flow, so users authenticate to *their* Paperclip, not to us.
49
- - **Scopes enforced by the bridge** (Paperclip can't): `paperclip:read` (reports, sync, lists) · `paperclip:control` (pause/resume/wake, issues, comments, goals) · `paperclip:admin` (terminate, approvals, budgets, raw non‑GET). Default consent = read. The bridge already classifies tools `read|write|destructive`; map those to scopes.
50
- - **Tokens:** short‑lived access JWT (audience = bridge URL), rotating refresh tokens, per‑user revocation (and revoke the Paperclip key on disconnect via `auth revoke-current`). Tokens bound to one company if the user picks one at consent.
51
- - **Registration:** support CIMD first (no client sprawl), DCR as fallback.
52
- - **Endpoints to add:** `/.well-known/oauth-protected-resource`, `/.well-known/oauth-authorization-server`, `/register`, `/authorize`, `/token`, `/revoke`; `401` challenge on `/mcp`.
53
- - **Multi‑tenancy:** every Paperclip instance has its own URL. Options: (a) *self‑hosted bridge per operator* (their own domain) — add as a **custom connector by URL**, no directory needed; (b) *one hosted multi‑tenant bridge* where the user supplies their instance URL at consent (needs SSRF‑safe URL validation + Claude "URL pattern"/custom‑connection mode, which takes longer to review); (c) *Paperclip Cloud* — a fixed URL pattern per stack, the cleanest directory story but needs Paperclip's cooperation.
54
- - Keep `BRIDGE_TOKEN` bearer mode for Claude Code headers, ChatGPT Actions, and the `static_headers` beta.
55
-
56
- ## 5. Phased plan
57
-
58
- | Phase | Deliver | Ship channel | Status |
59
- | --- | --- | --- | --- |
60
- | 1 | stdio MCP + Claude Code plugin + Actions bridge, read‑only mode, confirm gates | git marketplace now; npm publish; submit **Plugin bundle** to Anthropic's directory | **Done** (needs live-instance test, then submit) |
61
- | 2 | OAuth 2.1 AS in the bridge (§4), scopes, token store, audit log | custom connector by URL on claude.ai; ChatGPT app (dev mode → submission) | **Done** (self-hosted; DCR only; see `docs/oauth.md`). Untested against real claude.ai/ChatGPT clients |
62
- | 2b | **Multi-tenant public service**: per-user instance URL with SSRF-safe egress, Postgres store, key ring/rotation, idle revocation, Render blueprint, threat model | hosted bridge | **Built, untested live** (see SECURITY.md) |
63
- | 3 | Upstream: (i) PR/issue for **scoped board API keys**; (ii) optional Paperclip‑native plugin hosting the MCP endpoint behind Paperclip's own session auth | `paperclipai plugin install`; image catalog (`distribution/catalog.json`) | Later |
64
-
65
- Phase 3(ii) caveat **[unverified]**: plugin routes live only under `/api/plugins/:id/api/*` and can't claim root paths, while OAuth discovery wants `/.well-known/*` at the origin root (Claude lets the `401` point `resource_metadata` anywhere, but AS metadata must be at the issuer's well‑known path). A reverse‑proxy rule or a core change would be needed — test before committing to it.
66
-
67
- ## 6. Risks and open questions
68
-
69
- 1. **Never run against a live Paperclip yet.** Do that first, read‑only, then submit.
70
- 2. A hosted bridge holds users' board credentials: needs encryption at rest, a security review, a privacy policy and incident process. Prefer self‑hosting until then.
71
- 3. Prompt injection: issue/comment text from agents flows into the chat model. The skill tells Claude to treat it as data; consider output‑length and tag‑stripping limits.
72
- 4. Paperclip's API is alpha and changes fast; pin tested versions and add a contract test against a real instance in CI.
73
- 5. Directory policies and review criteria change; re‑read before each submission.
1
+ # Architecture, auth and distribution plan
2
+
3
+ Status: research + recommendation (Oct 2026). Sources: Paperclip source/docs (`paperclipai/paperclip`, read at depth‑1 clone) and Anthropic's connector/plugin docs. Items marked **[unverified]** are inferences or come from secondary sources and need testing before we rely on them.
4
+
5
+ ## 1. Verdict: integrate, don't rebuild
6
+
7
+ Keep Paperclip as the system of record and build *around* it. Rebuilding is not justified:
8
+
9
+ - Paperclip is a large, active codebase (8k+ files, ~295 DB migrations, adapters for ~10 agent runtimes, governance, budgets, approvals, MCP gateway, plugin SDK). Our goals — control, sync, reports, API use — are all reachable through its existing REST API. Nothing we need requires different core semantics.
10
+ - A fork forfeits upstream fixes and the Paperclip community's agents/templates (ClipHub). A rebuild would cost far more than the integration gaps below.
11
+ - The real gaps are **auth scoping** and **transport/OAuth**, and both can be solved in a front layer or contributed upstream.
12
+
13
+ ## 2. What Paperclip gives us (relevant facts)
14
+
15
+ | Area | Fact | Consequence |
16
+ | --- | --- | --- |
17
+ | API | REST under `/api`; company-scoped; JSON; errors `{error}` | Our bridge is a thin client — no DB access, no business logic |
18
+ | Auth modes | `local_trusted` (no login, loopback) · `authenticated/private` · `authenticated/public` (Better Auth sessions) | Bridge must work with no token (local) and with a token (everything else) |
19
+ | Operator creds | **Board API keys** (`board_api_keys`: hashed, `expiresAt`, `revokedAt`, `lastUsedAt`, bound to a *user*). **No scope or company column** | A board key = that user's full board power. Never hand one to a public third party |
20
+ | Login for tools | CLI challenge flow: client creates a challenge → user approves at `approvalUrl` in Paperclip's UI → client polls and receives a board token | Reusable as our "Sign in with Paperclip" step (no passwords touch our code) |
21
+ | Agent creds | Agent keys / run JWTs; pause, resume, approve etc. are **board‑only** | Operator control must use board creds (documented in README) |
22
+ | OAuth | Paperclip is an OAuth **client** (to upstream MCP servers). It is **not** an authorization server | claude.ai / ChatGPT connectors can't authenticate to Paperclip directly |
23
+ | MCP | Official `@paperclipai/mcp-server` is a stdio wrapper for use inside agent runs. "Gateway mode" is agent→upstream, with profiles/policies/audit. No operator‑facing remote `/mcp` | Our bridge is the missing operator-side endpoint |
24
+ | Plugins | Real plugin runtime: manifest + worker + UI; capability‑gated worker APIs; `apiRoutes` mounted only at `/api/plugins/:id/api/*`; plugins are trusted code; capability additions → `upgrade_pending` until an operator approves; install by npm/local path/image catalog (`paperclipai plugin install`). **No public plugin marketplace** (ClipHub = company templates only) | A native plugin is possible but distribution is operator‑installed, not store‑based |
25
+
26
+ ## 3. How public plugins actually ship (Claude & ChatGPT)
27
+
28
+ **Claude** (docs.claude.com connector + plugin docs):
29
+ - **Plugin bundle** (skills, commands, local MCP server like ours): public GitHub repo with `.claude-plugin/plugin.json`, README, LICENSE → `claude plugin validate` → submit at `claude.ai/directory/manage` ("Plugin bundle") → automated validation + security scan, sometimes human review → publish; later versions follow the tracked branch/tag. Data‑handling questions at submit time. Org can also share privately via a git marketplace (`/plugin marketplace add owner/repo`) — works today.
30
+ - **Remote MCP connector** (what claude.ai web/mobile/Desktop use): must be HTTPS. Auth = OAuth 2.0 (DCR or Client‑ID‑Metadata‑Document, PKCE S256, `401` + `WWW-Authenticate: Bearer resource_metadata=…`, RFC 9728 protected‑resource metadata, RFC 8414 AS metadata, form‑encoded `/token`, refresh rotation, 10 s endpoint latency), or none, or a **static header credential (beta, limited orgs)**. Callbacks: `https://claude.ai/api/mcp/auth_callback` and a port‑agnostic loopback for Claude Code. Every tool needs `title` + `readOnlyHint`/`destructiveHint` (we already set these). Reviewers need a populated test account. Custom (unlisted) connectors can be added by URL by any user/org without review.
31
+ - Plugins that reference a remote server should submit that server as a connector too.
32
+
33
+ **ChatGPT** **[unverified: from secondary sources; I could not fetch OpenAI's docs]**:
34
+ - *Apps SDK / MCP apps*: streamable‑HTTP MCP + OAuth 2.1 (DCR, PKCE); public submission needs a verified OpenAI org, public ToS + privacy policy, support contact, demo video, test account and test cases.
35
+ - *Custom GPT Actions*: OpenAPI + API‑key or OAuth; shareable via link or GPT Store. This is what we ship today (bearer key).
36
+
37
+ ## 4. Auth design for public use
38
+
39
+ Problem: board keys are all‑powerful, and the connector UIs want OAuth. Solution: put an **OAuth 2.1 authorization server inside the bridge** that fronts Paperclip.
40
+
41
+ ```
42
+ claude.ai / ChatGPT ──OAuth 2.1 + PKCE──▶ bridge (AS + resource server) ──board token──▶ Paperclip /api
43
+ │
44
+ /authorize ──▶ create Paperclip CLI-auth challenge ──▶ user approves in Paperclip UI
45
+ (bridge receives board token, stores it encrypted, mints its OWN scoped token)
46
+ ```
47
+
48
+ - **Sign‑in:** reuse Paperclip's challenge/approve flow, so users authenticate to *their* Paperclip, not to us.
49
+ - **Scopes enforced by the bridge** (Paperclip can't): `paperclip:read` (reports, sync, lists) · `paperclip:control` (every tool: pause/resume/wake/terminate, issues, comments, goals, approvals, budgets, raw non‑GET). Default consent = read. The bridge already classifies tools `read|write|destructive`; map those to scopes.
50
+ - **Tokens:** short‑lived access JWT (audience = bridge URL), rotating refresh tokens, per‑user revocation (and revoke the Paperclip key on disconnect via `auth revoke-current`). Tokens bound to one company if the user picks one at consent.
51
+ - **Registration:** support CIMD first (no client sprawl), DCR as fallback.
52
+ - **Endpoints to add:** `/.well-known/oauth-protected-resource`, `/.well-known/oauth-authorization-server`, `/register`, `/authorize`, `/token`, `/revoke`; `401` challenge on `/mcp`.
53
+ - **Multi‑tenancy:** every Paperclip instance has its own URL. Options: (a) *self‑hosted bridge per operator* (their own domain) — add as a **custom connector by URL**, no directory needed; (b) *one hosted multi‑tenant bridge* where the user supplies their instance URL at consent (needs SSRF‑safe URL validation + Claude "URL pattern"/custom‑connection mode, which takes longer to review); (c) *Paperclip Cloud* — a fixed URL pattern per stack, the cleanest directory story but needs Paperclip's cooperation.
54
+ - Keep `BRIDGE_TOKEN` bearer mode for Claude Code headers, ChatGPT Actions, and the `static_headers` beta.
55
+
56
+ ## 5. Phased plan
57
+
58
+ | Phase | Deliver | Ship channel | Status |
59
+ | --- | --- | --- | --- |
60
+ | 1 | stdio MCP + Claude Code plugin + Actions bridge, read‑only mode, confirm gates | git marketplace now; npm publish; submit **Plugin bundle** to Anthropic's directory | **Done** (needs live-instance test, then submit) |
61
+ | 2 | OAuth 2.1 AS in the bridge (§4), scopes, token store, audit log | custom connector by URL on claude.ai; ChatGPT app (dev mode → submission) | **Done** (self-hosted; DCR only; see `docs/oauth.md`). Untested against real claude.ai/ChatGPT clients |
62
+ | 2b | **Multi-tenant public service**: per-user instance URL with SSRF-safe egress, Postgres store, key ring/rotation, idle revocation, Render blueprint, threat model | hosted bridge | **Built, untested live** (see SECURITY.md) |
63
+ | 3a | **Paperclip plugin** (connection manager inside Paperclip, `plugin/`) | `paperclipai plugin install` from npm | **Built (v1.2.0-beta.1), untested in a live Paperclip** |
64
+ | 3 | Upstream: (i) PR/issue for **scoped board API keys**; (ii) optional Paperclip‑native plugin hosting the MCP endpoint behind Paperclip's own session auth | `paperclipai plugin install`; image catalog (`distribution/catalog.json`) | Later |
65
+
66
+ Phase 3(ii) caveat **[unverified]**: plugin routes live only under `/api/plugins/:id/api/*` and can't claim root paths, while OAuth discovery wants `/.well-known/*` at the origin root (Claude lets the `401` point `resource_metadata` anywhere, but AS metadata must be at the issuer's well‑known path). A reverse‑proxy rule or a core change would be needed — test before committing to it.
67
+
68
+ ## 6. Risks and open questions
69
+
70
+ 1. **Never run against a live Paperclip yet.** Do that first, read‑only, then submit.
71
+ 2. A hosted bridge holds users' board credentials: needs encryption at rest, a security review, a privacy policy and incident process. Prefer self‑hosting until then.
72
+ 3. Prompt injection: issue/comment text from agents flows into the chat model. The skill tells Claude to treat it as data; consider output‑length and tag‑stripping limits.
73
+ 4. Paperclip's API is alpha and changes fast; pin tested versions and add a contract test against a real instance in CI.
74
+ 5. Directory policies and review criteria change; re‑read before each submission.
@@ -0,0 +1,86 @@
1
+ # Community: Discord, Discussions and socials
2
+
3
+ A ready-to-follow setup for the Papercliped Discord, plus where everything else lives. Links show up on the website only once they're set (`web/src/config/community.ts`, or `VITE_DISCORD_URL`, `VITE_X_URL`, `VITE_DISCUSSIONS_URL`, `VITE_FORUM_URL` at build time). Nothing here needs a bot.
4
+
5
+ ## 1. Create the server (about 20 minutes)
6
+
7
+ 1. Discord → **+ → Create My Own → For a club or community**. Name: **Papercliped**. Icon: `web/public/brand/mascot-dark.svg` exported to PNG (or the 512px social image).
8
+ 2. **Server Settings → Enable Community.** This turns on rules screening, a rules channel and an announcements channel.
9
+ 3. **Safety Setup:** verification level **Medium** (verified email, on Discord 5+ minutes); explicit media filter **all members**; turn on **AutoMod** with "Block commonly flagged words", "Block mention spam" (limit 5) and a custom rule that blocks messages matching `pcs_`, `pcb_` and `sk-` (secret keys and tokens), with the reply "Never post secret keys. Make a new one in the Papercliped plugin."
10
+ 4. **Invite link:** never expires, no use limit. Put it in `VITE_DISCORD_URL` (Render env for the website build) and tell a maintainer to add it to `.github/ISSUE_TEMPLATE/config.yml`.
11
+
12
+ ## 2. Channels
13
+
14
+ | Category | Channel | Who can post | What for |
15
+ | --- | --- | --- | --- |
16
+ | **Start here** | `#rules` | Maintainers | Server rules (below); Community "rules screening" uses it |
17
+ | | `#welcome` | Maintainers | What Papercliped is, links to docs and install steps |
18
+ | | `#announcements` | Maintainers | Releases (automatic, below) and news; members can follow it into their own servers |
19
+ | | `#get-contributor` | Everyone | Post a link to your merged PR to get the Contributor role |
20
+ | **Community** | `#general` | Everyone | Chat |
21
+ | | `#help` (Forum channel) | Everyone | One post per question; tags: Claude, ChatGPT, Codex, Plugin, Self-hosting, Solved |
22
+ | | `#ideas` (Forum channel) | Everyone | Feature ideas; good ones become GitHub issues |
23
+ | | `#show-and-tell` | Everyone | What you built with your agents; the only place for self-promotion |
24
+ | **Build** | `#contributors` | Contributors and up | Coordinating work, reviews, design questions |
25
+ | | `#releases-feed` | Bot (webhook) | Every GitHub release, automatically |
26
+ | | `#maintainers` (private) | Maintainers | Moderation and security coordination; never discuss vulnerability details anywhere else |
27
+
28
+ ## 3. Roles
29
+
30
+ | Role | Colour | Given to | Permissions beyond default |
31
+ | --- | --- | --- | --- |
32
+ | **Maintainer** | Cream `#e9e4b0` | People with merge and release rights on GitHub | Manage messages, timeout members, post in Start here, see `#maintainers` |
33
+ | **Triager** | Blue | People with GitHub triage access | Manage threads in `#help`/`#ideas`, mark Solved |
34
+ | **Contributor** | Green | Anyone with a merged pull request | Post in `#contributors` |
35
+ | **Member** | default | Everyone who accepts the rules | Post in Community |
36
+
37
+ Keep the list of Maintainers on Discord identical to GitHub's. Remove roles when people step back.
38
+
39
+ ## 4. Rules (paste into `#rules`)
40
+
41
+ 1. Be kind. Follow the [code of conduct](https://github.com/OpenSourcx/papercliped/blob/main/CODE_OF_CONDUCT.md).
42
+ 2. **Never post secret keys, tokens or Paperclip addresses with credentials.** If you do by accident, make a new key in the Papercliped plugin straight away and tell a maintainer.
43
+ 3. Security problems go to support@papercliped.co or GitHub's private reporting, never a channel.
44
+ 4. One question per `#help` post, with what you tried and the error you saw.
45
+ 5. No spam or DMs to people you don't know. Self-promotion only in `#show-and-tell`.
46
+ 6. Stay on topic: Papercliped, Paperclip and running AI agents.
47
+ 7. Maintainers' moderation decisions are final; appeal by email to support@papercliped.co.
48
+
49
+ ## 5. Welcome message (paste into `#welcome`)
50
+
51
+ > **Welcome to Papercliped!** Papercliped connects Claude, ChatGPT, Codex and any MCP app to your Paperclip.
52
+ >
53
+ > • Install it: https://github.com/OpenSourcx/papercliped#add-it-to-your-ai-app
54
+ > • Docs: https://docs.papercliped.co
55
+ > • Stuck? Open a post in #help. Ideas go in #ideas.
56
+ > • Want to contribute? Read https://github.com/OpenSourcx/papercliped/blob/main/CONTRIBUTING.md, then grab a `good first issue`.
57
+ >
58
+ > Never post your secret key or tokens.
59
+
60
+ ## 6. Onboarding
61
+
62
+ **Server Settings → Onboarding:** default channels `#welcome`, `#general`, `#help`, `#announcements`. One question, "What do you use Papercliped with?" (Claude / ChatGPT / Codex / Plugin in Paperclip / Self-hosting). Each answer is just a tag for now; it helps us see where people come from.
63
+
64
+ ## 7. Releases into Discord automatically
65
+
66
+ Every push to `main` becomes a GitHub release (see [RELEASING.md](RELEASING.md)). To post them:
67
+
68
+ 1. In `#releases-feed`: **Edit Channel → Integrations → Webhooks → New Webhook**, copy its URL.
69
+ 2. GitHub → repository **Settings → Webhooks → Add webhook**. Payload URL: the Discord URL **with `/github` added at the end**. Content type `application/json`. **Let me select individual events → Releases** only.
70
+ 3. Optionally do the same for `#announcements`, so major and minor releases are visible there; patches stay in the feed.
71
+
72
+ ## 8. Becoming a contributor (how roles are given)
73
+
74
+ 1. Someone's pull request is merged.
75
+ 2. They post the link in `#get-contributor`.
76
+ 3. A maintainer checks it and gives the Contributor role (and adds them to the release thanks).
77
+
78
+ Triager and Maintainer are by invitation, decided by the maintainers together; see "From first PR to maintainer" in [CONTRIBUTING.md](../CONTRIBUTING.md).
79
+
80
+ ## GitHub Discussions (the forum)
81
+
82
+ Turn on **Discussions** in the repository settings with the categories Q&A, Ideas, Show and tell and Announcements. Then set `FORUM_URL=https://github.com/OpenSourcx/papercliped/discussions` on the bridge (Render), so `forum.papercliped.co` redirects there, and `VITE_DISCUSSIONS_URL` / `VITE_FORUM_URL` for the website. Pin a "Start here" discussion that links the README install steps.
83
+
84
+ ## X (social)
85
+
86
+ Create the account, set `VITE_X_URL`, and post major and minor releases from the changelog feed (`https://papercliped.co/changelog.xml`).
@@ -1,3 +1,3 @@
1
- # Deploying on Render
2
-
3
- The deployment guide now lives in **[LAUNCH.md](LAUNCH.md)** (Supabase → Render free → Cloudflare domain → keep-alive → panel → marketplaces → npm).
1
+ # Deploying on Render
2
+
3
+ The deployment guide now lives in **[LAUNCH.md](LAUNCH.md)** (Supabase → Render free → Cloudflare domain → keep-alive → panel → marketplaces → npm).
@@ -0,0 +1,74 @@
1
+ # Domains and hosts (papercliped.co)
2
+
3
+ One bridge service answers every host; it routes by the `Host` header. Nothing here needs a second server.
4
+
5
+ | Host | What it serves |
6
+ | --- | --- |
7
+ | `papercliped.co` | The website (landing, status, changelog, community, brand, privacy, terms) and every API |
8
+ | `www.papercliped.co` | Redirects to `papercliped.co` (same path) |
9
+ | `docs.papercliped.co` | The docs: the landing at `/` and each page at `/<page>` (e.g. `/permissions`); old `/docs/...` and `/topics/...` links redirect there |
10
+ | `mcp.papercliped.co` | **Where AI apps connect** (`/mcp`, the OAuth issuer), ChatGPT Actions and `/openapi.json`; web pages redirect to `papercliped.co` |
11
+ | `api.papercliped.co` | `/api/public/v1/*` and `/openapi.json`; web pages redirect to `papercliped.co` |
12
+ | `forum.papercliped.co` | Redirects to the forum (`FORUM_URL`; GitHub Discussions to start, see `docs/COMMUNITY.md`) |
13
+
14
+ Every host also answers the protocol and API paths (`/mcp`, `/authorize`, `/token`, `/register`, `/revoke`, `/.well-known/*`, `/actions/*`, `/openapi.json`, `/api/*`, `/healthz`, `/readyz`), so nothing breaks if an app or person uses another host for them.
15
+
16
+ ## 1. Cloudflare DNS
17
+ In Cloudflare → `papercliped.co` → **DNS → Records**, add (Render shows the exact target, `<service>.onrender.com`):
18
+
19
+ | Type | Name | Target | Proxy |
20
+ | --- | --- | --- | --- |
21
+ | CNAME | `@` | `<service>.onrender.com` | DNS only |
22
+ | CNAME | `www` | `<service>.onrender.com` | DNS only |
23
+ | CNAME | `docs` | `<service>.onrender.com` | DNS only |
24
+ | CNAME | `mcp` | `<service>.onrender.com` | DNS only |
25
+ | CNAME | `api` | `<service>.onrender.com` | DNS only |
26
+ | CNAME | `forum` | `<service>.onrender.com` | DNS only |
27
+
28
+ Start with **DNS only (grey cloud)** so Render can issue certificates. SSL/TLS mode: **Full (strict)**. If you later turn on proxying (orange cloud), set `BRIDGE_PROXY_HOPS=2` and keep challenges off the API paths (`docs/LAUNCH.md` §3C).
29
+
30
+ ## 2. Render
31
+ Render → the service → **Settings → Custom Domains → Add** each of: `papercliped.co`, `www.papercliped.co`, `docs.papercliped.co`, `mcp.papercliped.co`, `api.papercliped.co`, `forum.papercliped.co`. Click **Verify** on each and wait for the certificates.
32
+
33
+ ## 3. Environment (Render → Environment)
34
+ ```
35
+ PUBLIC_HOSTS=marketing=papercliped.co,www.papercliped.co;docs=docs.papercliped.co;api=mcp.papercliped.co,api.papercliped.co;forum=forum.papercliped.co
36
+ FORUM_URL=https://github.com/OpenSourcx/papercliped/discussions
37
+ ```
38
+ The first `marketing` host is the canonical one. Hosts you don't list (like `<service>.onrender.com`) still serve the whole site, so the service stays reachable while DNS changes. The Docker image builds the website and the bridge finds it at `/app/web/dist` (override with `WEB_DIST`).
39
+
40
+ ## 4. The OAuth issuer: mcp.papercliped.co (decided)
41
+ `BRIDGE_PUBLIC_URL` is the OAuth issuer **and** the resource AI apps connect to, so an app's connector URL must be `<BRIDGE_PUBLIC_URL>/mcp`. Papercliped uses:
42
+ ```
43
+ BRIDGE_PUBLIC_URL=https://mcp.papercliped.co
44
+ ```
45
+ Connector URL: **`https://mcp.papercliped.co/mcp`** (ChatGPT Actions: `https://mcp.papercliped.co/openapi.json`). The website and docs stay on `papercliped.co`; the docs show the connector URL through the `{{MCP}}` placeholder.
46
+
47
+ **Changing `BRIDGE_PUBLIC_URL` invalidates every existing token**, so everyone connected must connect again (at the time of the switch, only the owner). After switching:
48
+ - Claude: Settings → Connectors → remove the old connector, add `https://mcp.papercliped.co/mcp`. Claude Code: `claude mcp remove papercliped` then `claude mcp add --transport http papercliped https://mcp.papercliped.co/mcp`.
49
+ - ChatGPT: re-add the connector, or re-import `https://mcp.papercliped.co/openapi.json` into the GPT.
50
+ - The Paperclip plugin needs nothing: it talks to `/api/manage` and `/api/public`, which work on every host (its default bridge URL stays `https://papercliped.co`).
51
+
52
+ ## 5. Check
53
+ ```
54
+ curl -sI https://www.papercliped.co/status # 301 → https://papercliped.co/status
55
+ curl -sI https://docs.papercliped.co/ # 200 (the docs landing)
56
+ curl -sI https://papercliped.co/docs/permissions # 301 → https://docs.papercliped.co/permissions
57
+ curl -s https://api.papercliped.co/api/public/v1/status
58
+ curl -s https://mcp.papercliped.co/.well-known/oauth-authorization-server | head -c 200 # issuer: https://mcp.papercliped.co
59
+ curl -sI https://forum.papercliped.co/ # 302 → the forum
60
+ ```
61
+
62
+ On **Windows PowerShell**, `curl` is a different command: use `curl.exe` (built into Windows 10 and 11), and drop `| head -c 200`:
63
+ ```powershell
64
+ curl.exe -sI https://www.papercliped.co/status
65
+ curl.exe -sI https://docs.papercliped.co/
66
+ curl.exe -sI https://papercliped.co/docs/permissions
67
+ curl.exe -s https://api.papercliped.co/api/public/v1/status
68
+ curl.exe -s https://mcp.papercliped.co/.well-known/oauth-authorization-server
69
+ curl.exe -sI https://forum.papercliped.co/
70
+ ```
71
+ "Could not resolve host" means the DNS record is missing or still spreading; a certificate error means Render hasn't verified that domain yet; a `200` where a `301` is expected means `PUBLIC_HOSTS` isn't set (or the service hasn't redeployed).
72
+
73
+ ## Rollback
74
+ Remove `PUBLIC_HOSTS` and redeploy: every host serves the whole site again, with docs at `/docs`. To go back to the old Markdown pages entirely, set `WEB_DIST` to a folder that doesn't exist. DNS records and Render custom domains can stay.
package/docs/LAUNCH.md CHANGED
@@ -1,16 +1,15 @@
1
- # Launch guide — Papercliped v1.0.0-beta.1
1
+ # Launch guide — Papercliped v2
2
2
 
3
3
  Everything here costs **$0/month** (Render free, Supabase free, Cloudflare free). Do the steps in order. Nothing in this repo has been run against a live Render, Supabase, claude.ai or ChatGPT yet — treat the first pass as a staging run and finish with the smoke test (§8).
4
4
 
5
5
  Never paste database passwords, `service_role` keys or `BRIDGE_SECRET` into chats, issues or commits. They go only into the host's environment settings.
6
6
 
7
7
  ## 1. Supabase (database)
8
- 1. Open your project → **SQL Editor** → paste all of [`docs/supabase-schema.sql`](supabase-schema.sql) → Run. (This is the full schema: accounts, grants, tokens, audit log, community log, node health, privacy, panel views.)
9
- 2. Create the two least-privilege roles. Edit the passwords, then run [`docs/least-privilege.sql`](least-privilege.sql) (role `bridge_app`, used by the bridge) and [`docs/panel-role.sql`](panel-role.sql) (role `panel_ro`, **read-only**, used by the panel).
8
+ 1. Open your project → **SQL Editor** → paste all of [`docs/supabase-schema.sql`](supabase-schema.sql) → Run. (This is the full schema: accounts, grants, tokens, audit log, community log, node health, privacy, and the read-only `panel_*` views used by the separate operator dashboard.)
9
+ 2. Create the least-privilege role for the bridge: edit the password, then run [`docs/least-privilege.sql`](least-privilege.sql) (role `bridge_app`). The read-only role for the operator dashboard is set up from that dashboard's own (private) repository.
10
10
  3. **Project Settings → Database**: copy the **Transaction pooler** (port 6543) and **Session pooler** (port 5432) host/user details and the **SSL certificate** (CA). Build:
11
11
  - `DATABASE_URL` = transaction pooler as `bridge_app`
12
12
  - `DATABASE_MIGRATE_URL` = session pooler as the admin (`postgres.<ref>`) role — used only to apply future migrations at start
13
- - `PANEL_DATABASE_URL` = transaction pooler as `panel_ro`
14
13
  4. Free projects pause after about a week of inactivity. The bridge writes a heartbeat every 10 min while awake and the keep-alive in §5 touches `/readyz` (a database query) every 5 min, so it never goes quiet.
15
14
 
16
15
  ## 2. Render (the bridge) — Docker web service, no Blueprint
@@ -23,7 +22,7 @@ Blueprints are not needed (and the paid features are not used). The repo ships a
23
22
  |---|---|
24
23
  | `BRIDGE_OAUTH` | `1` |
25
24
  | `BRIDGE_MODE` | `multi` |
26
- | `BRIDGE_PUBLIC_URL` | `https://papercliped.kpnsolute.com` |
25
+ | `BRIDGE_PUBLIC_URL` | `https://mcp.papercliped.co` (the OAuth issuer; see [DOMAINS.md](DOMAINS.md)) |
27
26
  | `BRIDGE_SECRET` | output of `openssl rand -hex 32` — **back it up now**; losing it disconnects every user |
28
27
  | `DATABASE_URL` | transaction pooler as `bridge_app` (§1) |
29
28
  | `DATABASE_MIGRATE_URL` | session pooler, admin role |
@@ -39,16 +38,44 @@ Blueprints are not needed (and the paid features are not used). The repo ships a
39
38
 
40
39
  You can test the image anywhere with Docker: `docker build -t papercliped . && docker run --rm -p 10000:10000 -e PORT=10000 -e BRIDGE_TOKEN=$(openssl rand -hex 32) papercliped`.
41
40
 
42
- ## 3. Domain: papercliped.kpnsolute.com (Cloudflare)
43
- 1. Render → your service → **Settings → Custom Domains → Add** `papercliped.kpnsolute.com`. Render shows the CNAME target.
44
- 2. Cloudflare → `kpnsolute.com` → **DNS → Add record**: Type `CNAME`, Name `papercliped`, Target `<service>.onrender.com`, **Proxy status: DNS only (grey cloud)** at first.
45
- 3. Back in Render click **Verify**; it issues the TLS certificate (a few minutes). When `https://papercliped.kpnsolute.com/healthz` answers, you may turn the orange cloud on (SSL/TLS mode **Full (strict)**). If you do, set `BRIDGE_PROXY_HOPS=2` (Cloudflare + Render) and re-test sign-in rate limiting.
46
- 4. `BRIDGE_PUBLIC_URL` is the OAuth issuer and is baked into every token. **Set it to the final domain before connecting any client**; changing it later disconnects everyone.
41
+ ## 3. Domain: papercliped.co (Cloudflare, free plan)
42
+ The bridge answers at the root of the domain: `https://papercliped.co`.
43
+
44
+ **A. Put the domain on Cloudflare**
45
+ 1. Cloudflare dashboard → **Add a domain** → `papercliped.co` → choose the **Free** plan. Let it import any existing DNS records, then delete ones you don't recognise.
46
+ 2. Cloudflare shows two nameservers. At the company you bought the domain from, replace the domain's nameservers with those two. Turn off DNSSEC at the registrar first if it is on (re-enable it from Cloudflare later).
47
+ 3. Wait until Cloudflare says the domain is **Active** (minutes to a few hours).
48
+
49
+ **B. Connect it to Render**
50
+ 1. Render → your service → **Settings → Custom Domains → Add** `papercliped.co` (and `www.papercliped.co` if you want it). Render shows the target, `<service>.onrender.com`.
51
+ 2. Cloudflare → `papercliped.co` → **DNS → Records → Add record**:
52
+ - Type `CNAME`, Name `@`, Target `<service>.onrender.com`, **Proxy status: DNS only (grey cloud)**. (Cloudflare allows a CNAME at the root; it flattens it automatically.)
53
+ - Optional: Type `CNAME`, Name `www`, Target `<service>.onrender.com`, DNS only.
54
+ 3. Back in Render click **Verify**. Render issues the TLS certificate (a few minutes). Wait until Render shows the certificate as issued and `https://papercliped.co/healthz` answers.
55
+ 4. Cloudflare → **SSL/TLS → Overview**: set encryption mode to **Full (strict)**.
56
+ 5. Optional, after the certificate is issued: switch the records to **Proxied (orange cloud)**. If you do, set `BRIDGE_PROXY_HOPS=2` on Render (Cloudflare + Render) and re-test sign-in rate limiting. Grey cloud (DNS only) is fine and simpler.
57
+
58
+ **C. Don't let Cloudflare block AI apps**
59
+ Claude's and ChatGPT's servers call the bridge directly, so a challenge page breaks them. If the records are proxied:
60
+ - Leave **Bot Fight Mode** off, and don't use "I'm Under Attack" mode.
61
+ - If you add WAF or challenge rules, skip them for `/mcp`, `/actions/*`, `/openapi.json`, `/register`, `/token`, `/revoke`, `/authorize*`, `/.well-known/*`, `/api/manage/*` and `/healthz`, `/readyz`.
62
+ - Don't put Cloudflare Access in front of the bridge.
63
+
64
+ **D. Contact address (optional, free)**
65
+ Cloudflare → `papercliped.co` → **Email → Email Routing**: create `support@papercliped.co` forwarding to your inbox and verify the destination address (Cloudflare adds the MX, SPF and DKIM records; leave them alone, they sit beside the root CNAME/A record). Email Routing only receives and forwards; replying *as* support@ needs a separate sending service. Add a DMARC record (`_dmarc` TXT) so the domain can't be spoofed. support@ is also the security contact (docs/SECURITY.md). Then set `SITE_CONTACT=support@papercliped.co` on Render.
66
+
67
+ **More hosts (docs., mcp., api., forum.):** see [DOMAINS.md](DOMAINS.md) for the DNS records, Render domains, `PUBLIC_HOSTS` and the OAuth issuer decision.
68
+
69
+ **E. Point the bridge at the new domain**
70
+ 1. Render → Environment: `BRIDGE_PUBLIC_URL=https://mcp.papercliped.co` (where AI apps connect; the website stays on `papercliped.co`), then redeploy.
71
+ 2. `BRIDGE_PUBLIC_URL` is the OAuth issuer and is baked into every token. Changing it disconnects every app that connected under the old address; those users connect again with `https://mcp.papercliped.co/mcp`. Set it to the final domain before inviting more users.
72
+ 3. Keep-alive worker (§5): `deploy/wrangler.toml` already targets `https://papercliped.co/readyz`; redeploy it with `npx wrangler deploy`.
73
+ 4. Paperclip plugin: its default bridge URL is `https://papercliped.co` (instances that changed the setting keep their own value).
47
74
 
48
75
  ## 4. Check it works
49
76
  ```bash
50
- curl https://papercliped.kpnsolute.com/readyz # {"ok":true,...}
51
- curl https://papercliped.kpnsolute.com/.well-known/oauth-authorization-server | head
77
+ curl https://papercliped.co/readyz # {"ok":true,...}
78
+ curl https://papercliped.co/.well-known/oauth-authorization-server | head
52
79
  ```
53
80
 
54
81
  ## 5. Keep it awake (Cloudflare Worker, free)
@@ -59,39 +86,28 @@ cd deploy && npx wrangler login && npx wrangler deploy # edit TARGET_URL in
59
86
  It runs every 5 minutes (cron `*/5 * * * *`) against `/readyz`. Open the worker's URL once to see the last result. Alternative: a free UptimeRobot HTTP monitor on `/readyz` at a 5-minute interval. Free hosting can still have occasional cold starts — the first request after a platform restart may take about a minute.
60
87
 
61
88
  ## 6. Install paths for users
62
- - **Claude (claude.ai / Desktop / mobile):** Settings → Connectors → *Add custom connector* → `https://papercliped.kpnsolute.com/mcp`. Claude opens the Papercliped sign-in page.
63
- - **Claude Code:** `claude mcp add --transport http papercliped https://papercliped.kpnsolute.com/mcp`, or install the plugin: `/plugin marketplace add OpenSourcx/papercliped` then `/plugin install papercliped@papercliped`.
89
+ - **Claude (claude.ai / Desktop / mobile):** Settings → Connectors → *Add custom connector* → `https://mcp.papercliped.co/mcp`. Claude opens the Papercliped sign-in page.
90
+ - **Claude Code:** `claude mcp add --transport http papercliped https://mcp.papercliped.co/mcp`, or install the plugin: `/plugin marketplace add OpenSourcx/papercliped` then `/plugin install papercliped@papercliped`.
64
91
  - **npm / local stdio:** `npx papercliped` with `PAPERCLIP_API_URL` and `PAPERCLIP_API_KEY` set.
65
- - **ChatGPT:** custom MCP app with the same `/mcp` URL (where your plan offers it), or import `https://papercliped.kpnsolute.com/openapi.json` as a GPT Action (see [chatgpt.md](chatgpt.md)).
92
+ - **ChatGPT:** custom MCP app with the same `/mcp` URL (where your plan offers it), or import `https://mcp.papercliped.co/openapi.json` as a GPT Action (see [chatgpt.md](chatgpt.md)).
66
93
 
67
94
  First sign-in: pick a username (6–32 characters, at least one number or `.` `#` `_`), connect your Paperclip (public https URL; approve in Paperclip), and **save the secret key shown once**. Later: sign in with username + key, or reconnect through Paperclip approval and keep the same account. Tick *Appear anonymously* to be shown as an alias like `Ann02`.
68
95
 
69
- ## 7. The operator panel (separate program)
70
- The panel never sees bridge secrets and only has read access (`panel_ro`). Its environment is exactly:
71
- ```
72
- PANEL_ADMIN_TOKEN=<openssl rand -hex 32>
73
- PANEL_DATABASE_URL=<panel_ro URL> PANEL_DATABASE_SSL=verify PANEL_DATABASE_CA=<PEM>
74
- PANEL_ALLOWED_IPS=<your IP> # strongly recommended
75
- ```
76
- It refuses to start if `BRIDGE_SECRET` or `DATABASE_URL` is present.
77
- - **Easiest ($0, no extra hours):** run it on your own machine — `npx papercliped-panel` — and open http://127.0.0.1:3940.
78
- - **Always-on:** a second free Render service shares the 750 free hours and could suspend the bridge, so don't. Use another free host (Fly.io, Koyeb, a home server) or put it behind Cloudflare Tunnel + Cloudflare Access.
79
- - Preview with fake data: `PANEL_DEMO=1 PANEL_ADMIN_TOKEN=<24+ chars> npx papercliped-panel`.
80
-
81
- The panel shows system health (and turns critical if the bridge stops reporting — asleep, crashed or cut off from the database), latency percentiles, bridge-vs-Paperclip time, faults vs. caller errors, user count, flow success rate and the live log:
82
- `09:25:30 og.kpnwrld - joined cliped` · `… - left cliped` · `… - updated cliped`.
83
- Anonymous users appear as their alias. Operator commands: `npx papercliped-admin users | delete-user <name> | grants | revoke <id> | revoke-all --yes`.
96
+ ## 7. Watching the service
97
+ - **Public stats (no sign-in):** `https://papercliped.co/api/public/v1/status` and `/api/public/v1/stats?window=24h` give aggregate users, connections, sign-in success, request success rate, latency, error mix and load. The website's `/status` page shows them with charts. Nothing identifying is published (see the public API docs).
98
+ - **Operator dashboard:** lives in a separate, access-controlled service (its own private repository), not in this repo. It reads the same telemetry tables (`audit_events`, `user_events`, `node_samples`) and the read-only `panel_*` views, which the bridge keeps writing and the migrations keep providing.
99
+ - **Operator commands** (on a machine with the bridge's environment): `npx papercliped-admin users | delete-user <name> | grants | revoke <id> | revoke-all --yes`.
84
100
 
85
101
  ## 8. Smoke test (before announcing)
86
102
  1. Add the connector in claude.ai, create an account, connect a real Paperclip, ask "list my agents".
87
103
  2. Disconnect, reconnect with the secret key; reconnect again through Paperclip approval (same account).
88
- 3. Toggle anonymity; confirm the panel shows the alias for old and new entries.
89
- 4. Stop traffic for 20 min; confirm the Cloudflare worker keeps the service answering and the panel shows it healthy.
104
+ 3. Toggle anonymity; confirm the operator dashboard shows the alias for old and new entries.
105
+ 4. Stop traffic for 20 min; confirm the Cloudflare worker keeps the service answering and `/api/public/v1/status` says `ok`.
90
106
 
91
107
  ## 9. Marketplaces — submit only after the smoke test passes
92
108
  Requirements change; confirm each against the current official page before submitting.
93
109
 
94
- **Claude directory (connectors / plugins)** — have ready: public GitHub repo (done), plugin manifest [`.claude-plugin/plugin.json`](../.claude-plugin/plugin.json), the remote MCP URL `https://papercliped.kpnsolute.com/mcp`, OAuth callbacks (`https://claude.ai/api/mcp/auth_callback`, Claude Code's localhost loopback), privacy policy and terms (served by the bridge at `https://papercliped.kpnsolute.com/privacy` and `/terms`; set `SITE_CONTACT` to a real contact address), a support contact, tool descriptions with read/write annotations, and a **test account** with a demo Paperclip for reviewers. Submit through Anthropic's connector/plugin submission form linked from the Claude docs. Until listed, users install through the custom-connector or `/plugin marketplace add` paths above.
110
+ **Claude directory (connectors / plugins)** — have ready: public GitHub repo (done), plugin manifest [`.claude-plugin/plugin.json`](../.claude-plugin/plugin.json), the remote MCP URL `https://mcp.papercliped.co/mcp`, OAuth callbacks (`https://claude.ai/api/mcp/auth_callback`, Claude Code's localhost loopback), privacy policy and terms (served by the bridge at `https://papercliped.co/privacy` and `/terms`; set `SITE_CONTACT` to a real contact address), a support contact, tool descriptions with read/write annotations, and a **test account** with a demo Paperclip for reviewers. Submit through Anthropic's connector/plugin submission form linked from the Claude docs. Until listed, users install through the custom-connector or `/plugin marketplace add` paths above.
95
111
 
96
112
  **ChatGPT (apps / GPT Store)** — an MCP app needs a public https MCP endpoint with OAuth (you have it), a privacy policy URL, and domain verification in the OpenAI developer dashboard; a GPT with Actions needs the OpenAPI URL and OAuth settings (see [chatgpt.md](chatgpt.md)). Submission details for the apps directory are still moving — follow OpenAI's current "apps SDK / submit your app" guide.
97
113
 
@@ -101,7 +117,7 @@ npm login # account with 2FA
101
117
  npm run build && npm test
102
118
  npm publish --tag beta # first publish of "papercliped"; check the name is free first: npm view papercliped
103
119
  ```
104
- Users run `npx papercliped@beta`. With the repo secret `NPM_TOKEN` set, pushing a `v*` tag runs [`release.yml`](../.github/workflows/release.yml), which publishes with provenance and creates the GitHub release. If the name `papercliped` is taken, use a scope (`@kpnworld/papercliped`) and change `name` in `package.json`.
120
+ Users run `npx papercliped@beta`. Pushing a new version to `main` runs [`release.yml`](../.github/workflows/release.yml), which publishes with npm trusted publishing (no token) and creates the GitHub release; one-time setup is in [RELEASING.md](RELEASING.md). If the name `papercliped` is taken, use a scope (`@kpnworld/papercliped`) and change `name` in `package.json`.
105
121
 
106
122
  ## 11. GitHub release "v1.0.0-beta.1"
107
123
  Releases → **Draft a new release** → tag `v1.0.0-beta.1` (target `main`) → title *Papercliped v1.0.0-beta.1* → paste [`CHANGELOG.md`](../CHANGELOG.md) → tick **Set as a pre-release** → Publish.
@@ -0,0 +1,37 @@
1
+ # Releasing
2
+
3
+ Two npm packages ship from one version: `papercliped` (the bridge, repo root) and `papercliped-paperclip-plugin` (`plugin/`). Publishing uses **npm trusted publishing** (OIDC) from `.github/workflows/release.yml`: no npm token is stored in GitHub, and provenance is attached automatically.
4
+
5
+ Requirements: the repository is public, the workflow runs on GitHub-hosted runners, and GitHub Actions is enabled (and billable, if needed) for the `OpenSourcx` organization.
6
+
7
+ ## One-time setup (npmjs.com)
8
+ Trusted publishing can only be configured on a package that already exists.
9
+
10
+ **State as of v2.1.0:** both packages exist on npm (`papercliped` 1.1.0-beta.2, `papercliped-paperclip-plugin` 2.0.0), so step 1 is done. `papercliped-paperclip-plugin@2.0.0` predates the move of account management into the plugin and links with the old one-time codes, which the bridge no longer accepts: v2.1.0 replaces it. Do step 2 for the plugin before tagging if you want the workflow to publish it; otherwise publish 2.1.0 by hand and the workflow skips it.
11
+
12
+ 1. Publish the first version by hand, with 2FA (the plugin needs Node 24.11 or newer):
13
+ ```sh
14
+ npm login
15
+ npm --prefix plugin ci
16
+ cd plugin && npm publish --otp=<code> # builds first (prepublishOnly); publishes papercliped-paperclip-plugin@2.0.0
17
+ ```
18
+ If you publish the plugin by hand at 2.0.0, the release run skips it (that version already exists) and publishes only `papercliped`.
19
+ 2. For **each** package on npmjs.com: open the package → **Settings** → **Trusted publishing** → **GitHub Actions** and enter:
20
+ - Organization or user: `OpenSourcx`
21
+ - Repository: `papercliped`
22
+ - Workflow filename: `release.yml`
23
+ - Allow `npm publish`
24
+ 3. For each package, set **Publishing access** to *Require two-factor authentication and disallow tokens*, then revoke any old automation tokens.
25
+
26
+ `repository.url` in both `package.json` files must stay exactly `git+https://github.com/OpenSourcx/papercliped.git` (npm checks it against the workflow's repository), and `publishConfig` must stay `{ "access": "public" }`.
27
+
28
+ ## The website
29
+ The website (`web/`) is **not** in the npm package. The Docker image builds it (a separate stage) and the bridge serves it from `/app/web/dist`. The npm package's `papercliped-bridge` serves its built-in Markdown pages instead, unless you point `WEB_DIST` at a built copy (`npm --prefix web ci && npm --prefix web run build`).
30
+
31
+ ## Each release
32
+ Releases are automatic: **every push to `main` whose version isn't released yet becomes a release.** Nobody pushes tags.
33
+
34
+ 1. Add what changed under `## Unreleased` in `CHANGELOG.md`, then run `node scripts/version.mjs <major|minor|patch> "<title>"`. It bumps every file listed in `.release.json` (`package.json`, `plugin/package.json`, `.claude-plugin/plugin.json`, `.codex-plugin/plugin.json`, `plugin/src/manifest.ts`, the lockfiles), turns `## Unreleased` into `## vX.Y.Z — <title>` and records the date. Which number to bump, and how to write the commit description, is in the release skill (`.agents/skills/release/SKILL.md`).
35
+ 2. Push to `main`.
36
+
37
+ On every push to `main`, `.github/workflows/release.yml` reads the version. If the tag `vX.Y.Z` already exists it stops (an ordinary commit). Otherwise it checks that every version string and the changelog section agree, builds and tests the root and the plugin, packs and installs both packages as a user would (`scripts/check-packages.mjs`), publishes `papercliped` and `papercliped-paperclip-plugin` (a version with `-`, like `2.2.0-beta.1`, goes to the `beta` dist-tag, otherwise `latest`), and only then creates the tag `vX.Y.Z` on that exact commit together with the GitHub release, whose notes are the changelog section followed by every commit description since the previous release. A push that doesn't bump the version ships nothing and gets a warning in the run. Each publish step skips a version already on npm, and the tag is made last, so a failed run can be re-run from the Actions tab (or with **Run workflow**) once the cause is fixed.