Skip to content

feat(backend): platform server linking API for multi-platform CoPilot - #12615

Merged
ntindle merged 44 commits into
devfrom
feat/platform-bot-linking
Apr 21, 2026
Merged

feat(backend): platform server linking API for multi-platform CoPilot#12615
ntindle merged 44 commits into
devfrom
feat/platform-bot-linking

Conversation

@Bentlybro

@Bentlybro Bentlybro commented Mar 31, 2026

Copy link
Copy Markdown
Member

Why

AutoPilot (CoPilot) needs to reach users across chat platforms — Discord first, Telegram / Slack / Teams / WhatsApp next. To make usage and billing coherent, every conversation resolves to one AutoGPT account. There are two independent linking flows:

  • SERVER links: the first person to claim a server (Discord guild, Telegram group, …) becomes its owner. Anyone in the server can chat with the bot; all usage bills to the owner.
  • USER links: an individual links their 1:1 DMs with the bot to their own AutoGPT account. Independent from server links — a server owner still has to link their DMs separately.

What

Backend for platform linking, split cleanly by trust boundary:

  • Bot-facing operations run over cluster-internal RPC via a new PlatformLinkingManager(AppService). No shared bearer token; trust is the cluster network itself.
  • User-facing operations stay on REST under JWT auth (the same pattern as every other feature).

REST endpoints (JWT auth)

  • GET /api/platform-linking/tokens/{token}/info — non-sensitive display info for the link page
  • POST /api/platform-linking/tokens/{token}/confirm — confirm a SERVER link
  • POST /api/platform-linking/user-tokens/{token}/confirm — confirm a USER link
  • GET /api/platform-linking/links / DELETE /links/{id} — manage server links
  • GET /api/platform-linking/user-links / DELETE /user-links/{id} — manage DM links

PlatformLinkingManager @expose methods (internal RPC)

  • resolve_server_link(platform, platform_server_id) -> ResolveResponse
  • resolve_user_link(platform, platform_user_id) -> ResolveResponse
  • create_server_link_token(req) -> LinkTokenResponse
  • create_user_link_token(req) -> LinkTokenResponse
  • get_link_token_status(token) -> LinkTokenStatusResponse
  • start_chat_turn(req) -> ChatTurnHandle — resolves the owner, persists the user message, creates the stream-registry session, enqueues the turn; returns (session_id, turn_id, user_id, subscribe_from="0-0") so the caller subscribes directly to the per-turn Redis stream.

New DB models

  • PlatformLink(platform, platformServerId) → owner's AutoGPT userId
  • PlatformUserLink(platform, platformUserId) → AutoGPT userId (for DMs)
  • PlatformLinkToken — one-time token with linkType discriminator (SERVER | USER) and 30-min TTL

How

  • New backend/platform_linking/ package: models.py (Pydantic types), links.py (link CRUD helpers — pure business logic), chat.py (start_chat_turn orchestration), manager.py (PlatformLinkingManager(AppService) + PlatformLinkingManagerClient). Pattern matches backend/notifications/ + backend/data/db_manager.py.
  • Exception translation at the edge. Helpers raise domain exceptions (NotFoundError, LinkAlreadyExistsError, LinkTokenExpiredError, LinkFlowMismatchError, NotAuthorizedError — all ValueError subclasses in backend.util.exceptions so they auto-register with the RPC exception-mapping). REST routes translate to HTTP codes via a 7-line _translate() helper.
  • Independent scopes, no DM fallback. find_server_link() and find_user_link() each query their own table. A user who owns a linked server does not leak that identity into their DMs.
  • Race-safe token consumption. Confirm paths do atomic update_many with usedAt = None + expiresAt > now in the WHERE clause; create_*_token invalidates pending tokens before issuing a new one.
  • Bug fix: start_chat_turn persists the user message via append_and_save_message before enqueueing the executor turn — mirrors backend/api/features/chat/routes.py. The previous chat_proxy.py skipped this and ran the executor with no user message in history.
  • Streaming. Copilot streaming lives on Redis Streams (persistent, replayable). The bot subscribes directly with subscribe_from="0-0", so late subscribers replay the full stream; no HTTP SSE proxy needed.
  • No PII in logs: logs reference session_id, turn_id, server_id, and AutoGPT user_id (last 8 chars), but never raw platform user IDs.
  • New pod. PlatformLinkingManager runs as its own AppProcess on port 8009; client via get_platform_linking_manager_client(). The infra chart lands in cloud-infrastructure#310.

Tests

  • Models (models_test.py) — Platform / LinkType enums, request validation (CreateLinkToken / ResolveServer / BotChat), response schemas.
  • Helpers (links_test.py) — resolve, token create (both flows, 409 on already-linked), token status (pending / linked / expired / superseded-with-no-link), token info (404 / 410), confirm (404 / wrong flow / already used / expired / same-user / other-user), delete authz.
  • AppService wiring (manager_test.py) — @expose methods delegate to helpers; client surface covers bot-facing ops and excludes user-facing ones.
  • Adversarial (manager_test.py, routes_test.py):
    • asyncio.gather double-confirm with same user and with two different users — exactly one winner, other gets clean LinkTokenExpiredError, no double PlatformLink.create.
    • Server- and user-link confirm races.
    • TokenPath regex guard: rejects %24, URL-encoded path traversal, >64 chars; accepts secrets.token_urlsafe shape.
    • DELETE link_id with SQL-injection-style and path-traversal inputs returns 404 via NotFoundError.

Stack

Merge order: this → #12618#12624, infra whenever.

@coderabbitai

coderabbitai Bot commented Mar 31, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

This PR introduces platform bot account linking infrastructure with database models, API endpoints, and authentication to enable external platforms (Discord, Telegram, Slack, Teams, WhatsApp, GitHub, Linear) to establish verified connections with AutoGPT users and initiate CoPilot chat sessions through a token-based confirmation flow.

Changes

Cohort / File(s) Summary
Configuration & Build
.gitignore, backend/.env.default
Added exclusions for CoPilot bot dependencies and build output; introduced PLATFORM_BOT_API_KEY and PLATFORM_LINK_BASE_URL environment variables for platform linking configuration.
Database Schema
schema.prisma, migrations/20260331120000_add_platform_bot_linking/migration.sql, migrations/20260414160000_add_platform_user_links/migration.sql
Added PlatformType and PlatformLinkType enums; created PlatformLink (server linking), PlatformUserLink (DM/user linking), and PlatformLinkToken (token-based confirmation) models with foreign keys, uniqueness constraints, and indexes for efficient lookups and cascading deletes.
Platform Linking API Core
backend/api/features/platform_linking/__init__.py, backend/api/features/platform_linking/models.py, backend/api/features/platform_linking/auth.py, backend/api/features/platform_linking/routes.py
Defined Pydantic request/response schemas for token creation, confirmation, and link management; implemented bot API key header authentication with constant-time comparison; added FastAPI routes for server/user token lifecycle, link resolution, confirmation, listing, and deletion with transaction-based atomicity for token consumption.
Bot Chat Proxy
backend/api/features/platform_linking/chat_proxy.py
Implemented bot-facing endpoints for CoPilot session creation and streaming (POST /chat/session, POST /chat/stream) that resolve ownership via platform linkage, manage session state, and deliver responses as Server-Sent Events with keepalive comments and error handling.
API Integration & Tests
backend/api/rest_api.py, backend/api/features/platform_linking/routes_test.py, frontend/src/app/api/openapi.json
Registered platform-linking routers in FastAPI; added comprehensive test suite covering enum validation, Pydantic constraints, endpoint auth/error cases, token/link lifecycle, and transaction behavior; generated OpenAPI schema with new endpoints, security schemes, and component definitions.

Sequence Diagrams

sequenceDiagram
    participant Bot as External Bot Platform
    participant API as Platform Linking API
    participant DB as Database
    participant User as AutoGPT User

    rect rgba(100, 200, 100, 0.5)
    Note over Bot,User: Token-Based Linking Flow
    
    Bot->>API: POST /tokens (platform, platform_server_id)
    activate API
    API->>API: Authenticate (X-Bot-API-Key)
    API->>DB: Invalidate prior unused tokens
    API->>DB: Create PlatformLinkToken (30min expiry)
    API-->>Bot: LinkTokenResponse (token, link_url)
    deactivate API
    
    Bot->>User: Send link_url (embedded in user message)
    activate User
    User->>API: GET /tokens/{token}/info
    API->>DB: Fetch PlatformLinkToken
    API-->>User: LinkTokenInfoResponse (platform, link_type)
    
    User->>API: POST /tokens/{token}/confirm (JWT auth)
    activate API
    API->>DB: Verify token unused & unexpired
    API->>DB: Check no existing link for platform_server_id
    API->>DB: Atomically update token.usedAt & create PlatformLink
    API-->>User: ConfirmLinkResponse (success)
    deactivate API
    deactivate User
    end
Loading
sequenceDiagram
    participant Bot as External Bot Platform
    participant API as Chat Proxy API
    participant DB as Database
    participant CoPilot as CoPilot Service

    rect rgba(100, 150, 200, 0.5)
    Note over Bot,CoPilot: Bot-Driven Chat Session & Streaming
    
    Bot->>API: POST /chat/session (platform, platform_user_id, X-Bot-API-Key)
    activate API
    API->>API: Authenticate X-Bot-API-Key
    API->>DB: find_user_link(platform, platform_user_id)
    API->>CoPilot: create_chat_session(owner_user_id)
    API-->>Bot: BotChatSessionResponse (session_id)
    deactivate API
    
    Bot->>API: POST /chat/stream (platform_user_id, session_id, message, X-Bot-API-Key)
    activate API
    API->>API: Authenticate & resolve owner_user_id
    API->>DB: get_chat_session(session_id, owner_user_id)
    API->>API: Create StreamingResponse (SSE)
    API-->>Bot: Stream response header
    
    loop SSE Stream Chunks
        API->>CoPilot: subscribe_to_session()
        CoPilot-->>API: Stream chunk
        API-->>Bot: SSE event data
        Note over API,Bot: Keepalive sent every 30s (no data)
    end
    
    CoPilot-->>API: StreamFinish or [DONE]
    API->>API: Unsubscribe from stream
    API-->>Bot: SSE [DONE]
    deactivate API
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

Possible security concern, Review effort 4/5

Suggested reviewers

  • Pwuts
  • kcze
  • majdyz

Poem

🐰 Whiskers twitching with delight,
Platform links now shining bright!
Tokens dance through discord haze,
Bots and users in a craze!
Chat streams flow like morning dew,
AutoGPT's magic breaks on through! 🎉

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 31.48% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely describes the main change: adding a platform server linking API backend for multi-platform CoPilot integration, which is the primary focus of this changeset.
Description check ✅ Passed The PR description comprehensively explains the platform linking feature, implementation strategy, architecture decisions, and includes clear sections on Why, What, and How.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/platform-bot-linking

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions Bot added platform/backend AutoGPT Platform - Back end size/xl platform/frontend AutoGPT Platform - Front end labels Mar 31, 2026
@Bentlybro

Copy link
Copy Markdown
Member Author

/review

@autogpt-pr-reviewer

Copy link
Copy Markdown

Queued a review for PR #12615 at 7778de1.

Bentlybro added a commit that referenced this pull request Mar 31, 2026
Multi-platform bot service that deploys CoPilot to Discord, Telegram,
and Slack from a single codebase using Vercel's Chat SDK.

## What's included

### Core bot (src/bot.ts)
- Chat SDK instance with dynamic adapter loading
- onNewMention: resolves platform user → AutoGPT account
- Unlinked users get a link prompt via the platform-linking API
- Subscribed message handler with state management
- MVP echo response (CoPilot API integration next)

### Platform API client (src/platform-api.ts)
- Calls /api/platform-linking/resolve on every message
- Creates link tokens for unlinked users
- Checks link token status
- Chat session creation and SSE streaming (prepared for CoPilot)

### Serverless routes (src/api/)
- POST /api/webhooks/discord — Discord interactions endpoint
- POST /api/webhooks/telegram — Telegram updates
- POST /api/webhooks/slack — Slack events
- GET /api/gateway/discord — Gateway cron for Discord messages

### Standalone mode (src/index.ts)
- Long-running process for Docker/PM2 deployment
- Auto-detects enabled adapters from env vars
- Redis or in-memory state

## Stacked on
- feat/platform-bot-linking (PR #12615)

@autogpt-pr-reviewer autogpt-pr-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All 8 specialists have reported. Now I have everything I need. Let me compile the final verdict.

PR #12615 — feat: Platform bot linking API for multi-platform CoPilot
Author: Bentlybro | Requested by: ntindle | Files: routes.py (+381), routes_test.py (+25), rest_api.py (+6), migration.sql (+47), schema.prisma (+48), openapi.json (+212)

🎯 Verdict: REQUEST_CHANGES

What This PR Does

Adds a new backend subsystem for linking external chat platform identities (Discord, Telegram, Slack, etc.) to AutoGPT user accounts. This enables a multi-platform CoPilot bot to know which AutoGPT account a chat user belongs to. The flow: bot creates a one-time link token → sends user a URL → user clicks it, logs in, and confirms → the platform identity is now mapped to their account. The PR includes API routes, Prisma schema/migration, and OpenAPI spec updates. No frontend UI is included — that's planned as a follow-up.


Specialist Findings

🛡️ Security 🔴 — Three bot-facing endpoints (POST /tokens, GET /tokens/{token}/status, POST /resolve) ship with zero authentication. The code has a TODO acknowledging this (routes.py:103-108). Any internet caller can: (1) flood the token table via POST /tokens (no rate limiting, no cap per user), (2) enumerate linked users via POST /resolve by probing platform IDs — leaking internal user_id and platform_username, (3) poll any token's status and extract the linked user_id. Additionally, confirm_link_token (routes.py:210-263) performs 5 separate DB calls with no transaction — a race condition on double-click can trigger an unhandled unique constraint violation (500 error instead of clean 409).

  • 🔴 routes.py:98-108 — Unauthenticated bot endpoints: must add API key/service token auth before merge
  • 🔴 routes.py:210-263 — Race condition in confirm flow: no transaction wrapping check-and-consume
  • 🟠 routes.py:181-195/resolve leaks user_id to unauthenticated callers
  • 🟠 routes.py:158-160/tokens/{token}/status leaks user_id without auth
  • 🟠 routes.py:98-140 — Token flooding: unlimited pending tokens per platform user, no rate limit

🏗️ Architecture ⚠️ — Module structure follows the api/features/<name>/ convention correctly. Router registration is consistent. However: (1) Enum duplicationPlatformType is defined in Prisma schema AND as a Python VALID_PLATFORMS set (routes.py:34-41); these will drift when someone adds a platform to Prisma without updating the Python set. Fix: import from prisma.enums. (2) No service layer — other features (otto, mcp) separate routes from data access; this puts Prisma calls directly in handlers. (3) No expired token cleanup mechanism despite having an expiresAt index. (4) Hardcoded link_url base URL (routes.py:139) breaks staging/self-hosted. (5) Redundant @@index([token]) when @unique already creates an index on PlatformLinkToken.token.

  • ⚠️ routes.py:34-41 / schema.prisma — Enum duplication will drift
  • ⚠️ routes.py:139 — Hardcoded https://platform.agpt.co base URL
  • ⚠️ schema.prisma:1349 — Redundant index on token (unique already indexes)

Performance ✅ — All endpoints are O(1) indexed lookups, which is correct. The hot path (POST /resolve, called on every bot message) hits the composite unique index — efficient. Concerns: (1) confirm_link_token does 4 DB round trips without a transaction (also a correctness issue). (2) POST /tokens does a check-then-create (2 round trips) that could be a single upsert. (3) Redundant @@index([token]) doubles write cost on that column. (4) Token table grows unbounded — no TTL/cleanup.

  • ⚠️ schema.prisma:1349 — Remove redundant @@index([token])
  • ⚠️ No token cleanup job — table grows forever

🧪 Testing 🔴 — Only 25 lines of tests covering a single trivial helper (_validate_platform). Zero endpoint tests across all 6 API routes. The codebase has well-established patterns (TestClient + mock_jwt_user fixture + mocked Prisma) in sibling features (chat/routes_test.py, otto/routes_test.py, library/routes_test.py). Missing: happy-path linking flow, expired/used token handling, duplicate link prevention (409s), ownership checks on DELETE, auth enforcement on user-facing endpoints, race condition handling on concurrent confirm. Estimated coverage: ~5% of new code.

  • 🔴 routes_test.py — Only tests _validate_platform, zero endpoint coverage
  • Missing: ~20+ test cases for the 6 endpoints following existing codebase patterns

📖 Quality ⚠️ — Code is clean and well-structured with good module-level docstring explaining the flow. Issues: (1) LinkTokenStatusResponse.status is bare str — should be Literal["pending", "linked", "expired"] (routes.py:67). (2) delete_link returns dict instead of a response model (routes.py:310). (3) platform fields across all models are bare str — should use a shared Literal/enum type. (4) No max_length/min_length on platform_user_id (routes.py:54). (5) Logger uses f-strings instead of lazy %s formatting. (6) OpenAPI operation IDs are auto-generated and ugly (e.g., "getPlatform-linkingCheck if a link token has been consumed").

  • ⚠️ routes.py:67status: str → use Literal
  • ⚠️ routes.py:310-> dict → proper response model
  • ⚠️ routes.py:54 — No input length validation on platform_user_id

📦 Product ⚠️ — The backend API is well-designed for the linking flow, but the feature is unusable end-to-end: the hardcoded link_url points to https://platform.agpt.co/link/{token} which returns 404 because no frontend /link/{token} page exists yet. Error messages are developer-speak, not user-friendly (e.g., exposing internal IDs in 409 responses). The channelId field is stored but never used — hints at an unimplemented callback mechanism. Token expiry of 30 minutes is reasonable but tight for mobile users. No notification after linking completes — the bot must poll.

  • ⚠️ Frontend /link/{token} page missing — link is a dead end today
  • ⚠️ routes.py:120 — Error messages expose internal platform user IDs
  • ⚠️ routes.py:63channelId stored but never used (dead field)

📬 Discussion ✅ — CI passing (25/26 checks, only cosmetic skips). No human reviews submitted yet (reviewDecision: REVIEW_REQUIRED). CodeRabbit skipped (detected draft). Author's PR description explicitly lists follow-up TODOs: API key auth, frontend page, bot service, token cleanup, rate limiting. No linked tracking issues.

🔎 QA ✅ — All new API endpoints are functional and accessible. Live testing confirmed:

  • POST /resolve returns correct responses (200 for valid, 422 for invalid input, 400 for bad platform)
  • POST /tokens creates tokens with proper link_url and expires_at
  • GET /tokens/{token}/status returns pending/404 correctly
  • Auth-protected endpoints (GET /links, POST /confirm, DELETE /links/{id}) properly return 401 when unauthenticated
  • Frontend is completely unaffected — all pages (landing, copilot, library, build, marketplace) load correctly
  • No console errors related to this PR

Landing page
Dashboard after signup
Copilot chat
Library
Build page
Marketplace


Blockers (Must Fix)

  1. routes.py:98-108 — Bot-facing endpoints have NO authentication. POST /tokens, GET /tokens/{token}/status, and POST /resolve are completely open. Anyone can create tokens, enumerate linked users, and extract internal user IDs. The TODO comment is not an acceptable substitute. Add API key auth before merge. (Flagged by: Security 🔴, Architecture, Product, Discussion)

  2. routes.py:210-263 — Race condition in confirm_link_token. Five separate DB calls with no transaction. Concurrent requests can create duplicate links; the unique constraint violation surfaces as an unhandled 500 error. Wrap in a Prisma transaction or use atomic conditional update. (Flagged by: Security 🔴, Performance, Architecture)

  3. routes_test.py — Virtually no test coverage. Only 3 tests for a trivial helper. Zero endpoint tests across 6 routes and 381 lines of new code. The codebase has clear testing patterns that were not followed. Add at minimum: happy-path linking flow, expired/used token handling, duplicate prevention, auth enforcement. (Flagged by: Testing 🔴)

Should Fix (Follow-up OK)

  1. routes.py:34-41 / schema.prisma — Enum duplication. VALID_PLATFORMS Python set will drift from PlatformType Prisma enum. Import from prisma.enums instead. (Architecture, Quality)
  2. routes.py:139 — Hardcoded base URL. https://platform.agpt.co/link/{token} should be configurable via env var. Breaks staging/dev/self-hosted. (Architecture, Product)
  3. schema.prisma:1349 — Redundant @@index([token]). The @unique already creates an index. Remove to avoid doubled write cost. (Performance, Architecture)
  4. routes.py:67 — Loose typing. status: str should be Literal["pending", "linked", "expired"]. Similarly, platform fields across models should use a shared type. (Quality)
  5. routes.py:310delete_link returns dict. Only endpoint without a proper response model. Define DeleteLinkResponse. (Quality)
  6. Token table cleanupPlatformLinkToken rows accumulate forever. Add a periodic cleanup job. (Architecture, Performance)
  7. routes.py:54 — No input length validation on platform_user_id. A trivially long string would be persisted. Add Field(max_length=255, min_length=1). (Quality)
  8. routes.py:98-140 — Token flooding. No cap on pending tokens per (platform, platformUserId). Limit to 1 active token per identity. (Security, Performance)

Risk Assessment

Merge risk: HIGH | Rollback: EASY (new feature, no changes to existing tables/routes)

The core design is sound and the implementation is clean, but the three blockers — unauthenticated security-sensitive endpoints, a race condition, and near-zero test coverage — must be resolved before this merges. The feature is additive with easy rollback (drop the migration, remove the router), which reduces deployment risk.


REVIEW_COMPLETE
PR: #12615
Verdict: REQUEST_CHANGES
Blockers: 3

@github-project-automation github-project-automation Bot moved this from 🆕 Needs initial review to 🚧 Needs work in AutoGPT development kanban Mar 31, 2026
Bentlybro added a commit that referenced this pull request Mar 31, 2026
Adds the user-facing page that completes the platform bot linking flow.

When an unlinked user messages the bot, they get a URL like:
  https://platform.agpt.co/link/{token}

This page:
1. Validates the token (expired? already used?)
2. If user isn't logged in → redirects to login with ?next=/link/{token}
3. Shows a confirmation screen: 'Link your [platform] account to AutoGPT'
4. On click → calls POST /api/platform-linking/tokens/{token}/confirm
5. Shows success or error state

## Implementation
- Lives in (no-navbar) route group (standalone page, no main nav)
- Reuses AuthCard, Button, Text, Link components from existing auth pages
- Same visual style as login/signup pages
- Handles all edge cases: expired token, already linked, not authenticated

## Stacked on
- feat/copilot-bot-service (PR #12618)
- feat/platform-bot-linking (PR #12615)
Bentlybro added a commit that referenced this pull request Mar 31, 2026
Multi-platform bot service that deploys CoPilot to Discord, Telegram,
and Slack from a single codebase using Vercel's Chat SDK.

## What's included

### Core bot (src/bot.ts)
- Chat SDK instance with dynamic adapter loading
- onNewMention: resolves platform user → AutoGPT account
- Unlinked users get a link prompt via the platform-linking API
- Subscribed message handler with state management
- MVP echo response (CoPilot API integration next)

### Platform API client (src/platform-api.ts)
- Calls /api/platform-linking/resolve on every message
- Creates link tokens for unlinked users
- Checks link token status
- Chat session creation and SSE streaming (prepared for CoPilot)

### Serverless routes (src/api/)
- POST /api/webhooks/discord — Discord interactions endpoint
- POST /api/webhooks/telegram — Telegram updates
- POST /api/webhooks/slack — Slack events
- GET /api/gateway/discord — Gateway cron for Discord messages

### Standalone mode (src/index.ts)
- Long-running process for Docker/PM2 deployment
- Auto-detects enabled adapters from env vars
- Redis or in-memory state

## Stacked on
- feat/platform-bot-linking (PR #12615)
Bentlybro added a commit that referenced this pull request Mar 31, 2026
Adds the user-facing page that completes the platform bot linking flow.

When an unlinked user messages the bot, they get a URL like:
  https://platform.agpt.co/link/{token}

This page:
1. Validates the token (expired? already used?)
2. If user isn't logged in → redirects to login with ?next=/link/{token}
3. Shows a confirmation screen: 'Link your [platform] account to AutoGPT'
4. On click → calls POST /api/platform-linking/tokens/{token}/confirm
5. Shows success or error state

## Implementation
- Lives in (no-navbar) route group (standalone page, no main nav)
- Reuses AuthCard, Button, Text, Link components from existing auth pages
- Same visual style as login/signup pages
- Handles all edge cases: expired token, already linked, not authenticated

## Stacked on
- feat/copilot-bot-service (PR #12618)
- feat/platform-bot-linking (PR #12615)
@Bentlybro

Copy link
Copy Markdown
Member Author

/review

@autogpt-pr-reviewer

Copy link
Copy Markdown

Queued a review for PR #12615 at 781224f.

@autogpt-pr-reviewer autogpt-pr-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All 8 specialists have reported. Compiling the final verdict now.


PR #12615 — feat: Platform bot linking API for multi-platform CoPilot
Author: Bentlybro | Requested by: ntindle | Files: routes.py (+442), routes_test.py (+136), rest_api.py (+6), schema.prisma (+50), migration.sql (+44), openapi.json (+236)

🎯 Verdict: REQUEST_CHANGES

What This PR Does

Adds a backend API for linking external chat platform identities (Discord, Telegram, Slack, etc.) to AutoGPT user accounts. A bot creates a one-time link token, the user clicks it to log in and confirm, and the bot can then resolve the user's identity on subsequent messages. The Prisma schema, migration, and OpenAPI spec are all included. No frontend UI yet (backend-only first PR).

Specialist Findings

🛡️ Security ⚠️ — Three significant issues found.
⚠️ routes.py:74 — API key comparison uses Python != (not constant-time). Must use hmac.compare_digest() to prevent timing side-channel attacks on the bot API key.
⚠️ routes.py:56-68verify_bot_api_key is dead code with a stub body (# Check headerreturn None). Never called by any endpoint, but if someone wires it up by mistake, it would pass all requests. Remove entirely.
⚠️ routes.py:60-66, 71-73 — Default deployment (BOT_API_KEY="", ENV=development) has zero bot authentication. An attacker could create tokens, resolve identities, and poll status freely. Needs a loud startup warning or require explicit opt-in for dev bypass.
⚠️ routes.py:275-306 — TOCTOU race between token consumption and link creation. Two concurrent requests with different tokens for the same platform identity could both pass the find_first check; the DB unique constraint catches it but produces an unhandled 500 instead of a clean 409. Wrap in try/except.

🏗️ Architecture ✅ — Clean module structure follows existing backend/api/features/ patterns. Route registration is standard. Dual auth (API key for bot, JWT for user) is well-separated.
⚠️ routes.py:143-145 — Header extraction via Depends(lambda req: ...) is a hack that breaks FastAPI's dependency injection (see QA finding below). Should be a proper Header() dependency or typed function.
⚠️ No expired token cleanup mechanism — PlatformLinkToken rows accumulate forever. The @@index([expiresAt]) suggests cleanup was planned but never implemented.
⚠️ Python Platform enum manually mirrors Prisma PlatformType — adding a platform to one without the other causes runtime failures with no compile-time safety.

Performance ✅ — Indexes are appropriate for the query patterns. Unique constraint on (platform, platformUserId) covers the hot-path /resolve query.
⚠️ routes.py:234-255/resolve is called on every incoming bot message with no caching. A TTL cache (60-300s) would eliminate most DB hits since link status rarely changes.
⚠️ Missing composite index on PlatformLinkToken(platform, platformUserId) — both create_link_token and confirm_link_token query by this pair.
⚠️ routes.py:341-363list_my_links returns all results unbounded. Add pagination as a guardrail.
⚠️ routes.py:245, 160, 303 — Uses find_first where find_unique with the compound key would be more efficient and intentional.

🧪 Testing ⚠️ — Coverage estimated at ~15%. Tests exist for enum values, API key validation, Pydantic models, and response models — but zero route handlers are tested.
⚠️ No tests for: create_link_token, confirm_link_token, get_link_token_status, resolve_platform_user, list_my_links, delete_link
⚠️ No integration tests with TestClient — the broken Depends(lambda req: ...) pattern would have been caught immediately by any API-level test
⚠️ No tests for race condition handling, token expiry, 409 conflicts, or IDOR protection on delete_link
⚠️ routes_test.py:48,83,100 — Class-level imports (from ... import X inside class body, accessed via self.X) are non-idiomatic; use module-level imports

📖 Quality ⚠️ — Good documentation (module docstring, handler docstrings, schema comments). Pydantic models well-typed with descriptions.
⚠️ routes.py:56-70 — Dead verify_bot_api_key function alongside working _check_bot_api_key is confusing. Two auth functions with overlapping names but different approaches.
⚠️ routes.py:143-145# noqa: ARG005 on lambda dependencies signals a code smell — the lambda hack requires lint suppression.
⚠️ routes.py:49LINK_BASE_URL has no trailing-slash guard; f"{LINK_BASE_URL}/{token}" would produce double-slash if configured with trailing /.
⚠️ OpenAPI spec has spurious "name": "req", "in": "query", "required": true parameters on bot endpoints — a client-facing bug from the lambda Depends pattern.

📦 Product ✅ — The linking flow follows industry-standard patterns (similar to Slack OAuth linking). Token expiry of 30 minutes is reasonable. One-to-many (user → platforms) and one-to-one (platform identity → user) enforcement is correct. Error messages are user-friendly.
⚠️ channelId is stored in PlatformLinkToken but never used — no completion notification to the user on the platform side after linking. Acceptable as a follow-up.
⚠️ No frontend route for /link/{token} — users clicking the link will 404. Must be documented as a required follow-up.
⚠️ platformUsername has no update mechanism — goes stale if user changes their display name. Consider updating on each /resolve call.

📬 Discussion ✅ — PR is in draft state. One bot review (autogpt-pr-reviewer) requested changes with 3 blockers and 8 should-fix items. Bentlybro addressed most in follow-up commits (7bf10c6, 8a91bd8, 781224f). No human reviews yet. CI passing (26/27 checks, one pending/skipped). No merge conflicts.
⚠️ Still unaddressed from prior bot review: no rate limiting, no token cleanup, and the verify_bot_api_key dead code.

🔎 QA ❌ — CRITICAL BUG: All 3 bot-facing endpoints are non-functional.
🔴 routes.py:143-145, 179-181, 213-215Depends(lambda req: req.headers.get("x-bot-api-key")) does not work. FastAPI interprets the untyped req parameter as a required query parameter, not the Request object. Result: 422 "Field required" without ?req=, and 500 'str' object has no attribute 'headers' with it. POST /tokens, GET /tokens/{token}/status, and POST /resolve are all broken.
✅ User-facing endpoints work correctly: POST /tokens/{token}/confirm (401 without JWT, 404 for invalid tokens), GET /links (returns list, 401 without auth), DELETE /links/{id} (404 for nonexistent, 401 without auth)
✅ Frontend unaffected — landing page, login, signup, copilot dashboard, build page all load normally

landing
copilot-dashboard
copilot-loggedin
build-page

Blockers (Must Fix)

  1. routes.py:143-145, 179-181, 213-215 — All bot-facing endpoints are broken. The Depends(lambda req: req.headers.get("x-bot-api-key")) pattern fails because FastAPI cannot infer req is the Request. Fix:

    from fastapi import Request
    async def get_bot_api_key(request: Request) -> str | None:
        return request.headers.get("x-bot-api-key")

    Then use x_bot_api_key: str | None = Depends(get_bot_api_key) on each endpoint. (Flagged by QA ❌, Architecture 🏗️, Quality 📖)

  2. routes.py:74 — API key comparison must use hmac.compare_digest(request_api_key, BOT_API_KEY) instead of != to prevent timing side-channel attacks. (Flagged by Security 🛡️)

  3. routes.py:56-68 — Remove the dead verify_bot_api_key function entirely. It has a broken stub body and is never called, but could be mistakenly wired up as an auth dependency that passes everything. (Flagged by Security 🛡️, Architecture 🏗️, Quality 📖, Product 📦)

  4. routes.py:275-306 — Wrap the PlatformLink.prisma().create() in a try/except for UniqueConstraintViolation to handle the TOCTOU race gracefully (return 409 instead of 500). (Flagged by Security 🛡️, Architecture 🏗️, Performance ⚡)

Should Fix (Follow-up OK)

  1. routes.py:234-255 — Add a TTL cache on resolve_platform_user — this is called on every bot message and hits the DB each time.
  2. routes_test.py — Add integration tests for route handlers using TestClient. The lambda Depends bug would have been caught by any API-level test. At minimum: happy path for create/confirm/resolve/list/delete.
  3. migration.sql — Add composite index PlatformLinkToken(platform, platformUserId) for the queries in create_link_token and confirm_link_token.
  4. routes.py:341-363 — Add pagination to list_my_links (take/skip or cursor-based).
  5. routes.py:60-66, 71-73 — Add startup warning when BOT_API_KEY is empty, or require explicit DISABLE_BOT_AUTH=true for dev mode.
  6. Add a periodic cleanup job for expired PlatformLinkToken rows.
  7. Document the frontend /link/{token} route as a required follow-up in the PR description.
  8. openapi.json — The spurious "req" query parameter on bot endpoints will confuse API clients and generated SDKs. Resolves automatically when blocker #1 is fixed.

Risk Assessment

Merge risk: HIGH — Core feature endpoints (bot-facing) are non-functional due to the FastAPI dependency injection bug. If merged as-is, no bot could use this API.
Rollback: EASY — New feature with no changes to existing code paths. Drop migration, remove router, done.


REVIEW_COMPLETE
PR: #12615
Verdict: REQUEST_CHANGES
Blockers: 4

Bentlybro added a commit that referenced this pull request Mar 31, 2026
Multi-platform bot service that deploys CoPilot to Discord, Telegram,
and Slack from a single codebase using Vercel's Chat SDK.

## What's included

### Core bot (src/bot.ts)
- Chat SDK instance with dynamic adapter loading
- onNewMention: resolves platform user → AutoGPT account
- Unlinked users get a link prompt via the platform-linking API
- Subscribed message handler with state management
- MVP echo response (CoPilot API integration next)

### Platform API client (src/platform-api.ts)
- Calls /api/platform-linking/resolve on every message
- Creates link tokens for unlinked users
- Checks link token status
- Chat session creation and SSE streaming (prepared for CoPilot)

### Serverless routes (src/api/)
- POST /api/webhooks/discord — Discord interactions endpoint
- POST /api/webhooks/telegram — Telegram updates
- POST /api/webhooks/slack — Slack events
- GET /api/gateway/discord — Gateway cron for Discord messages

### Standalone mode (src/index.ts)
- Long-running process for Docker/PM2 deployment
- Auto-detects enabled adapters from env vars
- Redis or in-memory state

## Stacked on
- feat/platform-bot-linking (PR #12615)
Bentlybro added a commit that referenced this pull request Mar 31, 2026
Adds the user-facing page that completes the platform bot linking flow.

When an unlinked user messages the bot, they get a URL like:
  https://platform.agpt.co/link/{token}

This page:
1. Validates the token (expired? already used?)
2. If user isn't logged in → redirects to login with ?next=/link/{token}
3. Shows a confirmation screen: 'Link your [platform] account to AutoGPT'
4. On click → calls POST /api/platform-linking/tokens/{token}/confirm
5. Shows success or error state

## Implementation
- Lives in (no-navbar) route group (standalone page, no main nav)
- Reuses AuthCard, Button, Text, Link components from existing auth pages
- Same visual style as login/signup pages
- Handles all edge cases: expired token, already linked, not authenticated

## Stacked on
- feat/copilot-bot-service (PR #12618)
- feat/platform-bot-linking (PR #12615)
Bentlybro added a commit that referenced this pull request Mar 31, 2026
Multi-platform bot service that deploys CoPilot to Discord, Telegram,
and Slack from a single codebase using Vercel's Chat SDK.

## What's included

### Core bot (src/bot.ts)
- Chat SDK instance with dynamic adapter loading
- onNewMention: resolves platform user → AutoGPT account
- Unlinked users get a link prompt via the platform-linking API
- Subscribed message handler with state management
- MVP echo response (CoPilot API integration next)

### Platform API client (src/platform-api.ts)
- Calls /api/platform-linking/resolve on every message
- Creates link tokens for unlinked users
- Checks link token status
- Chat session creation and SSE streaming (prepared for CoPilot)

### Serverless routes (src/api/)
- POST /api/webhooks/discord — Discord interactions endpoint
- POST /api/webhooks/telegram — Telegram updates
- POST /api/webhooks/slack — Slack events
- GET /api/gateway/discord — Gateway cron for Discord messages

### Standalone mode (src/index.ts)
- Long-running process for Docker/PM2 deployment
- Auto-detects enabled adapters from env vars
- Redis or in-memory state

## Stacked on
- feat/platform-bot-linking (PR #12615)
Bentlybro added a commit that referenced this pull request Mar 31, 2026
Adds the user-facing page that completes the platform bot linking flow.

When an unlinked user messages the bot, they get a URL like:
  https://platform.agpt.co/link/{token}

This page:
1. Validates the token (expired? already used?)
2. If user isn't logged in → redirects to login with ?next=/link/{token}
3. Shows a confirmation screen: 'Link your [platform] account to AutoGPT'
4. On click → calls POST /api/platform-linking/tokens/{token}/confirm
5. Shows success or error state

## Implementation
- Lives in (no-navbar) route group (standalone page, no main nav)
- Reuses AuthCard, Button, Text, Link components from existing auth pages
- Same visual style as login/signup pages
- Handles all edge cases: expired token, already linked, not authenticated

## Stacked on
- feat/copilot-bot-service (PR #12618)
- feat/platform-bot-linking (PR #12615)
Bentlybro added a commit that referenced this pull request Mar 31, 2026
Multi-platform bot service that deploys CoPilot to Discord, Telegram,
and Slack from a single codebase using Vercel's Chat SDK.

## What's included

### Core bot (src/bot.ts)
- Chat SDK instance with dynamic adapter loading
- onNewMention: resolves platform user → AutoGPT account
- Unlinked users get a link prompt via the platform-linking API
- Subscribed message handler with state management
- MVP echo response (CoPilot API integration next)

### Platform API client (src/platform-api.ts)
- Calls /api/platform-linking/resolve on every message
- Creates link tokens for unlinked users
- Checks link token status
- Chat session creation and SSE streaming (prepared for CoPilot)

### Serverless routes (src/api/)
- POST /api/webhooks/discord — Discord interactions endpoint
- POST /api/webhooks/telegram — Telegram updates
- POST /api/webhooks/slack — Slack events
- GET /api/gateway/discord — Gateway cron for Discord messages

### Standalone mode (src/index.ts)
- Long-running process for Docker/PM2 deployment
- Auto-detects enabled adapters from env vars
- Redis or in-memory state

## Stacked on
- feat/platform-bot-linking (PR #12615)
Bentlybro added a commit that referenced this pull request Mar 31, 2026
Adds the user-facing page that completes the platform bot linking flow.

When an unlinked user messages the bot, they get a URL like:
  https://platform.agpt.co/link/{token}

This page:
1. Validates the token (expired? already used?)
2. If user isn't logged in → redirects to login with ?next=/link/{token}
3. Shows a confirmation screen: 'Link your [platform] account to AutoGPT'
4. On click → calls POST /api/platform-linking/tokens/{token}/confirm
5. Shows success or error state

## Implementation
- Lives in (no-navbar) route group (standalone page, no main nav)
- Reuses AuthCard, Button, Text, Link components from existing auth pages
- Same visual style as login/signup pages
- Handles all edge cases: expired token, already linked, not authenticated

## Stacked on
- feat/copilot-bot-service (PR #12618)
- feat/platform-bot-linking (PR #12615)
@Bentlybro

Copy link
Copy Markdown
Member Author

/review

@autogpt-pr-reviewer

Copy link
Copy Markdown

Queued a review for PR #12615 at a268291.

@github-actions github-actions Bot added the conflicts Automatically applied to PRs with merge conflicts label Mar 31, 2026
@github-actions

Copy link
Copy Markdown
Contributor

This pull request has conflicts with the base branch, please resolve those so we can evaluate the pull request.

test_create_token_for_unlinked_server was failing on CI with:
  RuntimeError: <asyncio.locks.Event object> is bound to a different event loop

create_link_token uses `async with transaction()` which opens a real
Prisma transaction — Prisma binds asyncio primitives (Event, Lock) to
whichever event loop first touches the client. pytest-asyncio runs each
test in its own loop, so by the second test the cached primitives
belong to a dead loop and any await against them blows up.

Swap in a fake asynccontextmanager that yields a MagicMock so the route
runs without touching the real client.
Adds 23 endpoint-level tests to raise patch coverage on the
platform_linking module:

- create_user_link_token: success + 409 conflict
- resolve_platform_user: linked + unlinked
- get_link_token_status: 404, pending, expired-by-time, used-with-link,
  used-but-superseded
- get_link_token_info: 404, used→404, expired→410, success
- confirm_link_token: 404, wrong link type, already used, expired,
  already-linked-to-same-user, already-linked-to-other-user
- confirm_user_link_token: 404, wrong link type, expired,
  already-linked-to-other-user

All tests patch find_*, transaction, and PlatformLinkToken so no real
DB is touched. Endpoints are invoked directly to exercise their full
control flow without bringing up a TestClient.
Comment thread autogpt_platform/backend/backend/api/features/platform_linking/chat_proxy.py Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
autogpt_platform/backend/backend/api/features/platform_linking/routes.py (1)

355-363: Catch UniqueViolationError directly instead of string-matching on error messages.

The code at lines 355–363 and 438–446 relies on "unique" in str(exc).lower(), which is fragile: unrelated errors containing the word "unique" (or localized/version-specific Prisma messages that change the substring) will be misclassified. Prisma exports a typed UniqueViolationError that the rest of the codebase uses for this exact pattern.

♻️ Proposed refactor
-from prisma.models import PlatformLink, PlatformLinkToken, PlatformUserLink
+from prisma.errors import UniqueViolationError
+from prisma.models import PlatformLink, PlatformLinkToken, PlatformUserLink

Lines 355–363:

     except HTTPException:
         raise
-    except Exception as exc:
-        if "unique" in str(exc).lower():
-            raise HTTPException(
-                status_code=409,
-                detail="This server was just linked by another request.",
-            ) from exc
-        raise
+    except UniqueViolationError as exc:
+        raise HTTPException(
+            status_code=409,
+            detail="This server was just linked by another request.",
+        ) from exc

Apply the same change to lines 438–446 for the DM-link handler.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@autogpt_platform/backend/backend/api/features/platform_linking/routes.py`
around lines 355 - 363, Replace the fragile string-match catch with a direct
exception handler for Prisma's UniqueViolationError: import UniqueViolationError
from prisma.exceptions (or prisma.errors if that’s used elsewhere in the repo)
and change the except block in the platform link handler (the block currently
checking "unique" in str(exc).lower()) to "except UniqueViolationError as exc:
raise HTTPException(status_code=409, detail='This server was just linked by
another request.') from exc"; keep the existing "except HTTPException: raise"
and the fallback "except Exception: raise" order. Make the identical change in
the DM-link handler (the similar block at lines 438–446) so both handlers catch
UniqueViolationError explicitly instead of string-matching.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@autogpt_platform/backend/backend/api/features/platform_linking/routes_test.py`:
- Around line 10-34: Move the module-level helper _fake_transaction so all
imports appear first: cut the asynccontextmanager _fake_transaction definition
and paste it below the import block that includes check_bot_api_key and the
platform_linking models (BotChatRequest, ConfirmLinkResponse,
CreateLinkTokenRequest, CreateUserLinkTokenRequest, DeleteLinkResponse,
LinkTokenStatusResponse, LinkType, Platform, ResolveResponse,
ResolveServerRequest, ResolveUserRequest) to resolve ruff E402.

In `@autogpt_platform/frontend/src/app/api/openapi.json`:
- Around line 9582-9589: The BotChatRequest schema currently allows
platform_server_id to be an empty string; update the backend model that emits
the OpenAPI (the BotChatRequest model) so platform_server_id is either null or a
non-empty string (e.g., Optional[str] with a min_length/validator that rejects
""), matching the other server-id fields; after changing the BotChatRequest
field definition and/or adding a validator for platform_server_id, regenerate
the OpenAPI JSON so autogpt_platform/frontend/src/app/api/openapi.json reflects
the non-empty constraint.
- Around line 6175-6190: The OpenAPI response for the SSE CoPilot endpoint is
incorrectly documented as application/json; update the backend route metadata
that generates the spec for the operationId "postPlatform-linkingStream a
copilot response for a platform user (bot-facing)" (request schema
BotChatRequest) so the 200 response content type is "text/event-stream" (or
equivalent SSE media type) with an appropriate simple/schema (e.g., string or
empty), e.g., by changing the route/handler decorator or OpenAPI response
annotation to produce "text/event-stream" (or adding an ApiResponse/content
entry for "text/event-stream") rather than application/json; regenerate the
frontend openapi.json rather than hand-editing it.

---

Nitpick comments:
In `@autogpt_platform/backend/backend/api/features/platform_linking/routes.py`:
- Around line 355-363: Replace the fragile string-match catch with a direct
exception handler for Prisma's UniqueViolationError: import UniqueViolationError
from prisma.exceptions (or prisma.errors if that’s used elsewhere in the repo)
and change the except block in the platform link handler (the block currently
checking "unique" in str(exc).lower()) to "except UniqueViolationError as exc:
raise HTTPException(status_code=409, detail='This server was just linked by
another request.') from exc"; keep the existing "except HTTPException: raise"
and the fallback "except Exception: raise" order. Make the identical change in
the DM-link handler (the similar block at lines 438–446) so both handlers catch
UniqueViolationError explicitly instead of string-matching.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 495824d6-1c5d-4188-8e87-c94126abe189

📥 Commits

Reviewing files that changed from the base of the PR and between 6cec213 and 724c90c.

📒 Files selected for processing (7)
  • autogpt_platform/backend/.env.default
  • autogpt_platform/backend/backend/api/features/platform_linking/chat_proxy.py
  • autogpt_platform/backend/backend/api/features/platform_linking/routes.py
  • autogpt_platform/backend/backend/api/features/platform_linking/routes_test.py
  • autogpt_platform/backend/migrations/20260331120000_add_platform_bot_linking/migration.sql
  • autogpt_platform/backend/migrations/20260414160000_add_platform_user_links/migration.sql
  • autogpt_platform/frontend/src/app/api/openapi.json
✅ Files skipped from review due to trivial changes (1)
  • autogpt_platform/backend/.env.default

Comment thread autogpt_platform/backend/backend/api/features/platform_linking/routes_test.py Outdated
Comment thread autogpt_platform/frontend/src/app/api/openapi.json Outdated
Comment thread autogpt_platform/frontend/src/app/api/openapi.json Outdated
Comment thread autogpt_platform/.gitignore Outdated
Comment thread autogpt_platform/backend/backend/api/features/platform_linking/auth.py Outdated
Comment thread autogpt_platform/backend/backend/api/features/platform_linking/chat_proxy.py Outdated
Comment thread autogpt_platform/backend/backend/api/features/platform_linking/chat_proxy.py Outdated
Comment thread autogpt_platform/backend/backend/api/features/platform_linking/chat_proxy.py Outdated
Comment thread autogpt_platform/backend/backend/api/features/platform_linking/routes.py Outdated
Comment thread autogpt_platform/backend/backend/api/features/platform_linking/routes.py Outdated
Comment thread autogpt_platform/backend/backend/api/features/platform_linking/routes_test.py Outdated
Comment thread autogpt_platform/backend/backend/api/rest_api.py
Nick round 1 + Sentry + CodeRabbit findings:

- .gitignore: drop `.claude/` (we share skills) and `copilot-bot/dist/`
  (Node.js artefact); only `.claude/settings.local.json` ignored.
- Auth consolidated onto FastAPI dependencies: every bot-facing route
  uses `dependencies=[Security(get_bot_api_key)]` on the decorator.
  get_bot_api_key validates inline. Dropped unused x_bot_api_key
  parameters and inline check_bot_api_key() calls.
- `/tokens/{token}/info` now JWT-authed via Security(auth.requires_user).
- Config pulled into Settings:
    Config.platform_link_base_url (was os.getenv in routes.py)
    Secrets.platform_bot_api_key (was os.getenv in auth.py)
- SSE /chat/stream advertises text/event-stream in OpenAPI responses.
- BotChatRequest.platform_server_id uses Field(min_length=1) so the
  no-empty-string constraint reaches the OpenAPI schema.
- bot_chat_stream calls mark_session_completed when subscribe returns
  None so the session doesn't sit in 'running' until TTL expires.
- Dropped X-Accel-Buffering: no — we use GCE ingress, not nginx.
- _fake_transaction test helper moved below imports (ruff E402).
- test_resolve_rejects_wrong_api_key removed — redundant now that auth
  runs at the FastAPI layer. Auth is tested in TestBotApiKeyAuth which
  now mocks Settings directly.

Regenerated openapi.json.
…ice RPC

Bot-facing platform_linking moves from REST with a shared X-Bot-API-Key
header to a new PlatformLinkingManager(AppService) reachable over
cluster-internal RPC. REST keeps only user-facing (JWT-authed) flows.

- New backend/platform_linking/ package: models, links (CRUD helpers),
  chat (start_chat_turn orchestration), manager (AppService + Client).
- Bot-facing REST endpoints, auth.py, chat_proxy.py, and
  PLATFORM_BOT_API_KEY are deleted.
- Domain exceptions (LinkAlreadyExistsError / LinkTokenExpiredError /
  LinkFlowMismatchError) added to backend.util.exceptions; REST layer
  translates them to HTTP status codes.
- start_chat_turn persists the user message via append_and_save_message
  before enqueueing, fixing a bug where the bot proxy kicked off turns
  with no user message recorded.
- New Config.platform_linking_service_port=8009, client factory
  get_platform_linking_manager_client(), pyproject script
  platform-linking-manager.
- Adversarial tests cover the confirm-token race (asyncio.gather with
  same-user and cross-user racers, no hijack), TokenPath regex guard on
  both GET and POST token paths, and DELETE link_id handling of weird
  inputs.
Comment thread autogpt_platform/backend/backend/platform_linking/chat.py Outdated
…OpenAPI

- chat.start_chat_turn previously called stream_registry.create_session
  even when append_and_save_message flagged the message as a duplicate,
  leaving an orphan Redis stream with no producer. Matching REST chat
  behaviour, skip create_session + enqueue and raise
  DuplicateChatMessageError so the caller can drop cleanly.
- Regenerate frontend/src/app/api/openapi.json to match the trimmed
  platform-linking REST surface.
Comment thread autogpt_platform/backend/backend/api/features/platform_linking/routes.py Outdated
…nager

Per Nick's review and the existing pattern used by blocks / notifications /
other AppServices: only DatabaseManager (owns pool) and AgentServer (main
REST pod) hold their own Prisma connection. Every other service goes through
DatabaseManagerAsyncClient so there's a single connection pool per cluster.

- Move Prisma code from platform_linking/links.py to platform_linking/db.py
  (pure DB layer). find_*_link now return scalar user_id instead of Prisma
  model objects, which were rejected by AppService's result validator.
- Add platform_linking_db() to backend/data/db_accessors.py following the
  existing auto-switch pattern (direct Prisma when db.is_connected(),
  DatabaseManagerAsyncClient otherwise).
- Expose the 14 platform_linking DB operations on DatabaseManager +
  DatabaseManagerAsyncClient.
- PlatformLinkingManager drops its lifespan Prisma connect; routes
  everything through platform_linking_db() which resolves to the RPC
  client in the manager pod.
- REST routes + chat.py go through the same accessor — same code path on
  AgentServer (direct) and PlatformLinkingManager (RPC).
- Tests move with the code: db_test.py replaces links_test.py; manager /
  chat / routes tests mock the accessor instead of Prisma.
…ocal-default link URL

- Replace fragile `"unique" in str(exc).lower()` with typed UniqueViolationError
  catch in both confirm_server_link and confirm_user_link (matches the pattern
  used in backend/copilot/db.py, backend/data/credit.py, backend/util/workspace.py).
- Point PLATFORM_LINK_BASE_URL in .env.default at http://localhost:3000/link
  so local and staging runs don't mint links pointing at production.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

platform/backend AutoGPT Platform - Back end platform/frontend AutoGPT Platform - Front end size/xl

Projects

Status: ✅ Done
Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants