Skip to content

[personal-proxy] /personal/entities/* proxy routes (wisdom → wisdom-agents) #36

@artugro

Description

@artugro

Context

Needed for the Intuno Personal product surface (wisdom-agents#62 epic, ADR at wisdom-agents#71). Frontend users authenticate against wisdom (JWT), but entity state lives in wisdom-agents. Today there's no auth bridge — wisdom-agents only accepts a shared X-API-Key: AGENTS_API_KEY, which a user-facing UI can't safely hold.

This ticket creates the proxy tier in wisdom so the frontend talks to exactly one public origin (wisdom), which validates JWTs, enforces quotas/billing, and forwards to wisdom-agents over the private network with the shared key + X-User-Id header.

Architecture

Frontend (wisdom JWT)
    → wisdom  POST /personal/entities                   [validate JWT, enforce quotas]
       → wisdom-agents  POST /entities                  [trusts X-API-Key + X-User-Id]

wisdom-agents remains a private internal service. wisdom is the only public API surface for Personal.

Scope — new routes under src/routes/personal.py

Proxy routes (JWT-protected, current_user injected):

  • POST /personal/entities — forward body to wisdom-agents POST /entities; attach X-User-Id: {current_user.id}. Enforce per-user entity-count quota before forwarding.
  • GET /personal/entities — forward; wisdom-agents scopes by X-User-Id.
  • GET /personal/entities/{name} — forward + ownership check.
  • PATCH /personal/entities/{name} — forward + ownership check.
  • DELETE /personal/entities/{name} — forward + ownership check.
  • POST /personal/entities/{name}/pause — forward.
  • POST /personal/entities/{name}/resume — forward.
  • POST /personal/entities/{name}/messages — forward to wisdom-agents POST /entities/{name}/chat (new — see wisdom-agents trust ticket).
  • GET /personal/entities/{name}/messages — forward to wisdom-agents GET /entities/{name}/chat.

Implementation notes

  • New service: src/services/personal.py — one PersonalAgentsClient class with an httpx.AsyncClient configured with INTUNO_AGENTS_BASE_URL + INTUNO_AGENTS_API_KEY env vars.
  • Routes are thin: validate ownership, forward, return response. No business logic in the proxy layer.
  • Errors: 4xx from wisdom-agents pass through with the same status code; 5xx gets logged and returns 502 with a scrubbed message.
  • Timeouts: 30s default; callback-sensitive endpoints (POST .../messages) can bump higher when we add streaming.

Quota enforcement (MVP)

Reject POST /personal/entities with 429 if the user already has N entities (default 1 on Free, 3 on Pro — fetch from users.plan once that exists; for MVP, env-configurable PERSONAL_FREE_TIER_ENTITY_CAP).

Per-message rate limits come later (#60 on wisdom-agents side).

Environment variables

  • INTUNO_AGENTS_BASE_URL — e.g., http://wisdom-agents:8001
  • INTUNO_AGENTS_API_KEY — the AGENTS_API_KEY value from the wisdom-agents deployment

Acceptance criteria

  • All 9 proxy routes wired and tested against a local wisdom-agents instance
  • JWT validation: anonymous request returns 401
  • Ownership enforcement: user A cannot GET/PATCH/DELETE user B's entity (403)
  • Quota: creating an (N+1)th entity returns 429
  • Error passthrough: wisdom-agents 404 surfaces as 404 at the proxy
  • OpenAPI spec regenerated

Blocked by

  • wisdom-agents [personal-trust] — needs the X-User-Id trust model live on the other side

Unblocks

Files (anticipated)

  • src/routes/personal.py (new)
  • src/services/personal.py (new) — PersonalAgentsClient
  • src/core/settings.py — add the two env vars
  • src/main.py — mount the router

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions