## Thinking Path > - Paperclip manages AI agents and their work. > - Slack connectors turn authenticated messages into company work. > - Direct callbacks need an awake tenant. > - Cloud can retain authenticated asynchronous events until the tenant wakes. > - Replay must preserve the original callback proof and current endpoint authority. > - Configuration changes must retain registration responsibility through failures. > - This PR enrolls verified Slack identities and drains sealed events through the normal SDK owner. ## Linked Issues or Issue Description **Subsystem affected** Server chat connectors and signed Cloud connector delivery. **Problem or motivation** Direct Slack Events API delivery depends on an awake tenant. Broker outages, lost responses and configuration changes must preserve delivery and registration responsibility. Enrollment failures must also leave other connector events able to drain. **Proposed solution** Stage nonsecret enrollment or retirement intent inside the local configuration transaction. Let the existing connection-event owner perform broker requests and retries. Replay sealed events through the current Slack SDK with their original authentication evidence. **Alternatives considered** An early acknowledgment for every callback changes command and modal semantics. A second delivery ledger duplicates existing ownership. Reusing a new stack identity to retire old registrations crosses an authorization boundary. A companion Cloud broker change is required. Related setup fixes: Refs #18837, Refs #18878. Kickoff searches found no competing async enrollment consumer. The ROADMAP's connector setup and access controls stay in place. ## What Changed - Persist enrollment and credential-free retirement intent before local configuration commits. Broker failure does not make a completed pause or removal look unsuccessful. - Bind company, endpoint, connection, public ID, generation and authorized actor. Preserve lost-response responsibility and recover only the supported same-instance configuration outage. - Permit an explicit move to a new authorized stack only after confirmed old retirement. Keep the old identity until the new broker receipt matches the current intent and generation. - Show permanent signing-credential failures as attention. Preserve transient retries and reject uncertain inbound authentication before SDK admission. - Drain events and acknowledgments before enrollment reconciliation. Persist a bounded retry on the exact busy intent so other due endpoints can enroll or retire. Preserve current custody under concurrent changes. - Stage retirement in the Apps removal transaction too. Archive the endpoint and connection, retain the original subject and instance, then revoke credentials. Retirement does not need the deleted Slack credential. - Acknowledge a malformed sealed Slack receipt as invalid once. Keep actual broker outages on the retry path. - Run the real configuration, SDK ingress, generation fences and public Apps removal paths in the default SQL suite. Keep Vitest transport helpers outside production services. - Preserve original callback bytes, signature, time and public URL evidence. Use the existing transactional chat-delivery ledger for redelivery. - Provide a checked-in prewire guard and launcher using the current Node executable. Add broker, worker, SDK, SQL and controlled-process acceptance. - Stabilize AI account summaries by ID and grant ID. This fixes a CI migration-repeat equality failure without changing SQL, authorization or reconnect behavior. - Open the four configured database connections before the enrollment test freezes its clock. Keep all 35 original assertions and all timeouts. - Measure status-card dispatch entry before its mock does database work. Keep the original strict 500 ms bound and check any still-armed earlier deadline. Bound that fixture's pool to four connections. ## Verification - Candidate: `54fa701d41b4ced62f4ff338d39b946015670ae3`. Normal merges preserve master `47027e7` and all owned changes. A read-only merge assessment against fetched master `cb5d7e44` is conflict-free. The only overlapping file has separate AI test additions. The branch has not merged that later master; fresh remote checks must validate the current PR test tree. - At this head, normal server production typechecking and declaration emission passed in 35.24s. The affected actual CI test shard passed in 6.27s. Other unchanged CI layers, 92 affected pure controls and 12 guard controls retain their source-matched results. - All **15 default real-PostgreSQL cases passed in 34.66s**, with no skips or filtering. They cover normal configure and SDK ingress, rollback, stale generations, malformed receipts, busy-lease fairness, concurrent intent custody, unknown errors, and public Apps removal. Removal tests check actual credential revocation, retained retirement through outage and repeat removal, one ordinary retry and confirmed retirement, transaction rollback and foreign-company refusal. - The public removal counterfactual failed before the fix: archival and credential revocation passed, but the inactive intent was absent. The same test source passed after the staging call. Separate counterfactuals also reproduced busy-endpoint starvation and malformed-receipt retries. Original failures remain retained. - The paired broker/SDK/process journey passed in 31.98s at ancestor `0324c011`. It preserves callback proof, lost-ACK redelivery, exactly one comment, one delivery and one successful controlled process. Its test, producer, SDK, guard and runtime bytes are unchanged. The new public Apps archive branch is not called by that journey; its evidence carries on that checked basis. - The new SQL cases need no companion Cloud checkout. Normal CI runs them with the repository fixture. For an existing dedicated PostgreSQL fixture, set `PAPERCLIP_SLACK_OWNER_TEST_DATABASE_URL` and `PAPERCLIP_TEST_POSTGRES_ADMIN_URL`, then run `node server/scripts/test-slack-cloud-owner.mjs` with Node24. The optional paired launcher still requires the normally built companion Cloud checkout. - Every pool was bounded to four or fewer connections. The final 15-case run sampled three runtime clients plus one observer. The independent final audit found zero clients and all five recorded processes absent. Earlier closing-socket observations are kept separate from final cleanup. - Earlier unrelated CI failures are documented: [enrollment timeout](https://github.com/paperclipai/paperclip/actions/runs/41944373937/job/126973483754) and [status-card observation bound](https://github.com/paperclipai/paperclip/actions/runs/41944373937/job/126973483843). The enrollment test retains all 35 assertions and its original timeouts. A controlled connection probe established the frozen-clock mechanism; the original remote access history remains unknown. The status-card fixture measures dispatch entry with the original strict 500ms bound. A real delayed-dispatch control fails that bound. - Full local workspace typecheck, build and test inventory was not run because its embedded-cluster and wider resource paths exceed this local lease. Exact-head remote CI now passes: 54 successful checks, three skips and Ponytail's neutral check, with aggregate verification green and the legacy security context independently confirmed successful. Both complete bot reviews are 5/5, with all 13 paginated review threads resolved. Seven execution jobs originally reported runner shutdown; one diagnostic rerun recovered them and the dependent aggregate check. Original logs remain retained, and the underlying shutdown cause is unknown. This is recovery evidence, not a product-fix claim. - Provider responses are controlled. This proves local SQL, broker, SDK and process behavior. It does not prove live Slack, agent tools, synchronous callbacks or deployed suspension. ## Risks The companion broker is required. Outage or conflicting authority can leave explicit pending or attention responsibility. A move between stacks requires confirmation from the old authorized owner first. Synchronous commands, actions, modal validation, options and challenges retain their deadlines and endpoint blocker. This slice grants no full Slack endpoint sleep exemption. No deployed suspension or savings claim is made. ## Model Used OpenAI `gpt-6.1-sol` workers through Codex, with reasoning, repository tools and code execution. The served backend revision and context limit are not exposed. A GPT-6-family coordinator performed technical review and integration. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing> Co-authored-by: Codex <noreply@openai.com>
Apps, Connections, and Integrations
Audience: internal engineers and product contributors working on integrations.
Start here when adding a provider: Connection authoring runbook. It is the canonical agent tutorial from provider research and protocol classification through manifest generation, branding, secrets, deterministic tests, real-account proof, and PR submission.
Chat and email channels: conversation-channel contract, question inventory and quality bar, and per-provider question coverage index.
Runtime authentication: AI Connections.
Long-term memory: Experimental memory connectors.
Provider notes: Google Workspace, Gmail, Asana, Sentry, PostHog, Braintrust, Neon, Superagent, Gauge, Ditto, 1Password, Unikraft, AgentMail, Telem.AI, iMessage Photon, Enterpret, Supabase, and Vercel. Optional credential custody: Vercel Connect.
Post-read action: classify a new integration request, pick the right Paperclip layer to change, and avoid creating a parallel connection framework.
Decision Record
Board decisions from PAP-13211 make the Apps v2 substrate on the PAP-10341 branch canonical:
- D1: Apps v2 is the substrate. The active model is
tool_applications,tool_connections, catalog entries, profiles, policy rules, action requests, gateway sessions, audit events, and runtime slots. Connections v1 is retired as an implementation path. - D2: one credential authority per connection, brokered projections. The
default is the Paperclip instance vault: durable third-party credentials live
in
company_secretsas secret refs. A reviewed remote MCP method may instead opt in to Vercel Connect, in which case Vercel is the durable credential authority and Paperclip stores only the connector reference and redacted grant metadata. A connection must never mix those two sources. Adapter config, plugin config, harness credential files, and run environments may receive only brokered or projected credentials. - D3: the vocabulary and three-door IA are product law. The default product doors are Apps, Connections, and Review. Protocol and operator-depth concepts live behind Developer or Advanced surfaces.
- D4: unification lands on PAP-10341. Pages, CircleBack-style harness MCP OAuth, provider gallery work, and plugin-provided integrations converge on this branch instead of spawning new integration substrates.
- D5: inbound stays thin. External clients that call Paperclip use scoped Paperclip tokens and existing profiles/rules. They do not get a separate permission model.
Canonical Object Model
Use connection as the unifying noun. A connection is four things:
- A stored credential reference.
- A capability catalog.
- A governance layer.
- An audit trail.
Everything else is an axis on that object:
| Axis | Values | It answers |
|---|---|---|
| Direction | outbound, inbound | Who is the client? |
| Transport | MCP, native REST/OpenAPI, OAuth app install, webhook | How do bytes move? |
| Auth mode | OAuth, API key/PAT, app installation, none | What does the secret represent? |
| Credential owner | company, user, run | Whose identity acts? |
| Packaging | catalog entry, plugin, skill | How does it ship? |
MCP is a transport, not a product category. "Install the Discord app", "connect Google Drive", and "add an MCP endpoint" all produce governed connections with different transport/auth values.
Layer Stack
When you are unsure where a change belongs, place it on the narrowest layer that solves the problem:
| Layer | Owns | Examples |
|---|---|---|
| Surface | user-facing Apps, Connections, Review, Developer/Advanced screens | gallery cards, setup wizard, review queue |
| Governance | profiles, bindings, allow/ask-first/block rules, quarantine, audit | read-only profile, ask-first write policy |
| Capability | action catalogs, schemas, risk classes, changed-tool review | search_issues, create_comment, schema hash |
| Credential | company_secrets, OAuth broker, credential resolver, token broker |
Slack bot token ref, Google OAuth refresh token ref |
| Identity | actor attribution and token exchange | board user, agent run, first-party service identity |
| Transport | how the external system is reached | remote HTTP MCP, local stdio, REST/OpenAPI, webhook |
The agent should not hold a durable provider credential. It should hold a Paperclip run/session token; the server or broker resolves the connection, checks governance, invokes the provider, and writes audit.
Identity vs. connections
Signing a user in and connecting a resource are different planes with different owners, different token profiles, and different homes. Do not merge them. This section is the public, connections-side statement of the identity model so connector implementers inherit the rule without depending on private identity-service documentation or re-deriving it.
| Plane | Question | Lives where | Token profile |
|---|---|---|---|
| P1. Sign-in methods | Who are you? | paperclip-id (id.paperclip.ing → Account) |
Minimal-scope provider tokens (openid email profile), used once to authenticate, encrypted at rest, never exported |
| P2. Connections (Apps) | What may your agents touch? | Paperclip App instances (tool_connections), acquired via the connect broker for hosted + self-hosted |
Rich-scope, long-lived resource tokens in the instance's encrypted vault; per-agent grants; risk-tier policy defaults |
| P3. Login with Paperclip | Who may authenticate against us? | paperclip-id OIDC provider + DB-backed client registry |
Our ES256 ID/access tokens issued by us to registered RPs (instances, the broker, future third parties) |
Everything in doc/connections/ — the First-30 matrix,
the connection authoring runbook, and the connect-broker work —
lives on plane P2. It never acquires, stores, or brokers a P1 sign-in token.
The standing rule (D7)
Adopted as a standing rule (decision D7) with the identity-model plan. State it verbatim in any P2 design so the app-store work cannot drift into merging the planes:
Sign-in tokens are never reused as resource tokens; id.paperclip.ing never stores resource tokens; no connections hub on the ID service.
P2 tokens flow broker → instance vault as pass-through only; the id.paperclip.ing Account page therefore must not grow a "Connections" hub. The reasons to hold the planes apart (from the plan §3):
- Scope discipline. Sign-in wants the narrowest grant; connections want deliberately broad ones. One button that does both is how you grant repo access just to log in.
- Blast radius. id.paperclip.ing holding every customer's Vercel/Slack/GitHub resource tokens would make it the single juiciest target in the fleet; the broker is intentionally pass-through.
- Self-hosted symmetry. Instances own their vaults, so self-hosters don't depend on our uptime to use their own connections.
- Legibility. Sign-in and connections answer different user questions, and every product we benchmarked (Vercel, Railway, GitHub, Google) keeps them on separate pages with separate names.
The explicit Vercel Connect exception does not change D7 or merge P1 and P2.
The operator chooses Vercel as the P2 credential authority for an individual
connection. id.paperclip.ing is not involved, and neither sign-in tokens nor
provider tokens pass through it. The deployment's Vercel access token or
workload OIDC identity is bootstrap authority for that external vault, not a
provider resource credential.
Naming alignment
Use the surface-correct name for each plane; they intentionally differ:
| Surface | Plane | Name to use |
|---|---|---|
| Paperclip App instances | P2 | "Connections" |
| id.paperclip.ing Account | P1 | "Ways to sign in" |
| id.paperclip.ing admin | P3 | "OIDC clients" (until the app store productizes it) |
Packaging Rule
Default to a catalog entry when an integration can be described as metadata: manifest, auth config, action catalog, resource filters, and policy defaults.
Use a plugin only when the integration needs product code such as custom UI pages, its own tables, workers, migrations, routines, or specialized ingestion. A plugin may bundle catalog entries, but it must not bypass the connection, profile, policy, credential, and audit model.
Use a skill for agent instructions. Skills may use connections; they must not own durable tokens.
Canonical Docs
- Glossary defines product and internal terms.
- Identity vs. connections is the public statement of the P1/P2/P3 boundary and the D7 standing rule for connections work.
- Security threat model harvests the keeper from PAP-2359 and maps it onto Apps v2.
- First-30 matrix harvests the keeper from PAP-2432 and is the source matrix for connector playbook work.
- Connecting any remote MCP server is the baseline: how an operator connects a standards-compliant remote MCP endpoint with no Paperclip code change, and how sign-in resolves a client.
- Connection authoring runbook is the one end-to-end, agent-executable guide for adding a vendor as a catalog entry on Apps v2: research, connection-type selection, OAuth/API-key/generated-URL setup, encrypted credential handling, branding, implementation, browser and live-provider testing, verification, and PR submission.
- Vercel Connect operator guide documents the optional external credential source, deployment flags, runtime resolution, recovery, and smoke requirements.
- MCP access governance remains the operator runbook for the current gateway, profile, policy, approval, runtime, and audit APIs.
Migration Notes
Connections v1 contributed useful policy, UX, and rollout thinking, but its
implementation branch is no longer the target. When you see old tickets or code
using connections, connection_grants, or a provider-directory mental model,
translate the intent into Apps v2:
| Connections v1 intent | Apps v2 home |
|---|---|
| Provider directory | Apps gallery / tool_applications |
| Configured provider instance | Connection / tool_connections |
| Grant allowlist | Profiles, profile bindings, policies |
| Resource filters | Policy/profile conditions plus provider config |
| Tool broker | Tool gateway and runtime supervisor |
| Connection UX tail | Apps, Connections, Review, Developer/Advanced IA |
Do not add new work to the retired v1 branch. If an old ticket still describes a valid product gap, retarget it to an active Apps v2 issue or close it as superseded with a link to the replacement.
For Slack chat bot creation, installation, recovery, and live qualification, see Automatic Slack app setup.