Poppy Company gateway¶
Status: Current | Last reviewed: 2026-10-20
Use this gateway to expose a reviewed, published Chat Agent through Poppy 0.2. Read this page when publishing an endpoint, testing a new agent version or operating the gateway.
The gateway is a separate FastAPI process on port 3036, implemented in
apps/conversation-api/poppy.
It reuses conversation-api's gRPC client and the published TrueServe runtime.
Its protocol ledger lives in trueserve_poppy, outside the core schema.
Initial Company profile¶
This release provides guest sessions and native text/data conversations. It does not advertise Direct/Device Sign-In, customer scopes, Operations, MCP, OpenAPI, browser continuity or a staffed human queue. A Studio user identity never becomes a Company customer identity.
Only an explicitly published text_to_text / chat_agents version is eligible.
The publisher must review it for unauthenticated guest use: prompts, knowledge,
tools and returned data must suit that access. Use a tool-free sandbox first.
Authentication-sensitive and transactional agents need the account and
business-authorization work in the roadmap.
Each exposure gets an independently routed hostname and issuer. Requests cannot choose another internal agent ID. The initial client admission list contains only the configured Ari origin. Client metadata fetching rejects local, link-local and other non-public addresses, including DNS rebinding.
Provision and publish¶
Load the city environment and initialize the two separate databases:
bash .devcontainer/scripts/with-env.sh node .devcontainer/scripts/init-poppy-company.mjs --configure-studio
The provisioner writes apps/conversation-api/.env.poppy with mode 0600 and
applies poppy/schema.sql to its application and test databases. The
--configure-studio option also writes the registry connection and domain
suffix to the root and ai-manager private environment files. It preserves
unrelated settings.
Start the gateway with .devcontainer/Procfile.poppy alongside an existing
city, or use the main Procfile when starting the whole city. Render/install
the city nginx template for poppy-*.<PUBLIC_FQDN> and verify public TLS.
Publication uses the existing management API with agents:publish. Send:
POST /api/v1/management/poppy/exposures
Authorization: Bearer <scoped Studio credential>
Content-Type: application/json
{
"agentId": "4992",
"expectedVersion": 2,
"name": "Ari Test Company",
"guestReviewed": true
}
Use your agent's actual published version. The API rejects a stale version, another tenant's agent and an unsupported agent type. Its response gives the stable exposure ID and discovery URL. This is an API/operator publication flow; a Studio publication settings screen is not yet implemented.
The operator CLI is an alternative. From apps/conversation-api, with the city
and .env.poppy settings loaded, run:
.venv/bin/python -m poppy.provision \
--tenant-id <tenant-uuid> --agent-id <agent-id> --version <published-version> \
--name "Test Company" --guest-reviewed
Keep the connection strings and signing secret in private files. Do not place credentials in command arguments.
Updates and rollback¶
An exposure pins the exact reviewed agent version. Publishing a new Studio
version alone does not update the endpoint. Review the new version and repeat
the publication API with its expectedVersion; the hostname stays the same.
New conversations use that version, while existing conversations and their
direct children keep their original runtime version.
To compare updates, export a protocol run, publish the reviewed update, choose
Rediscover in Ari and run again. The nonstandard
x-trueserve.agent_version discovery annotation records the version in the
snapshot. Use the CLI's explicit --version to return an exposure to an older
published version after reviewing it. Core agent versions remain unchanged.
Disable an endpoint with:
DELETE /api/v1/management/poppy/exposures/<exposure-uuid>
Authorization: Bearer <scoped Studio credential>
Disabling blocks new requests and workers that have not yet sent their message. It preserves the ledger. Work already accepted by a runtime may finish; disabling an endpoint is not a business-operation cancellation mechanism.
Wire and runtime behavior¶
| Path | Behavior |
|---|---|
/.well-known/poppy.json |
Discovery for the enabled, reviewed hostname |
/.well-known/oauth-authorization-server/oauth |
Issuer metadata and admitted grant type |
/oauth/token |
private_key_jwt, JWT bearer session assertion, ES256 DPoP, nonce and replay checks |
/oauth/revoke |
Authenticated no-op for unknown account grants; this profile issues none |
/poppy/conversations |
Accept a message and durably queue its runtime delivery |
/poppy/conversations/<id>/messages |
Append an owner message; original ID retries return original acceptance |
/poppy/conversations/<id>/events |
Cursor pagination and bounded JSON long polling |
/poppy/conversations/<id>/handoff |
Honest unavailable response; responder remains the Company agent |
/poppy/conversations/<id>/close |
Idempotent close and detached runtime cleanup |
Guest session identity is (exposure, client_id, opaque user subject), with a
fixed DPoP key per session. A fresh session for the same principal can resume its
conversation; another user or Company cannot. Tokens live for five minutes and
sessions for seven days in this profile.
Message acceptance, immutable user event, retry fingerprint and pending job
commit together. IDs are deduplicated across the principal's conversations.
Open direct children accept only sender=human; their parents reject sends.
Closing a parent also closes its direct child.
The worker uses a stable UUID runtime conversation and a pinned agent:vN,
with transcript resumption after a transport reconnect. It stores a Redis
cursor, reads final Company transcripts, and persists protocol events. Started
jobs interrupted by a restart are not resent automatically. The ledger records
uncertainty so a transport failure cannot silently duplicate an action.
Configuration and verification¶
| Variable | Purpose |
|---|---|
POPPY_DATABASE_URL |
Separate protocol registry/ledger; also used by Studio's publication API |
POPPY_NONCE_KEY |
Random server-only nonce secret, at least 32 characters |
POPPY_DOMAIN_SUFFIX |
Operator-configured city domain, used only to generate hostnames |
POPPY_ALLOWED_CLIENT_ORIGINS |
Exact public Personal Agent origins admitted by the gateway |
POPPY_TEST_DATABASE_URL |
Test connection; test runner forces trueserve_poppy_test |
| Existing runtime env | Core database, Redis and orchestrator connection settings |
The gateway takes an exclusive database lease and runs four independent conversation jobs at a time. An owner can have ten open conversations per exposure. Protocol bodies are capped at 64 KiB; long polling is capped at 30 seconds. This is a single-process pilot, not a horizontal worker deployment.
From apps/conversation-api:
.venv/bin/python test_poppy.py
The tests exercise actual FastAPI routes, signatures and the separate PostgreSQL ledger. They use a controlled runtime fixture. The hosted Ari check additionally exercises the real model and gRPC runtime.