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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions