OttoBot Security
| Document status | Public documentation |
| Target audience | Enterprise security & compliance teams |
| Last updated | 2026-08-21 |
This page describes how per-org OttoBot — the conversational assistant available to your organization inside Phoenix — is deployed, how data moves through it, how requests are authenticated and bound to your organization, and which controls keep one customer's activity separated from another's.
It is written for a security reviewer performing an assessment. For the broader Phoenix platform, see AI Governance; for the technical detail of input handling, redaction, and adversarial testing, see Security Guardrails.
Scope of this document
This document provides a technical and architectural overview of the per-org OttoBot integration. It describes current system design and operational practices.
This document is not:
- A contractual agreement (see your HG Insights MSA for binding terms)
- A substitute for HG Insights Corporate InfoSec documentation (SOC 2, penetration testing, etc.)
- A guarantee of specific SLAs (see service agreement)
Specific contractual commitments, audit rights, and liability terms are negotiated separately in customer agreements.
Not covered here: embedding OttoBot in your own application via iframe. That surface has a separate authentication model and will be documented separately.
1. Integration architecture
Per-org OttoBot is a chat surface inside the Phoenix web application. It is not a separate product you log into, and it is not reachable from the public internet as a standalone service.
A conversation turn moves through three components:
┌──────────────┐ ┌─────────────────┐ ┌──────────────────┐
│ Browser │────▶│ Phoenix webapp │────▶│ OttoBot service │
│ (your user) │ │ (chat route) │ │ (in-cluster) │
└──────────────┘ └─────────────────┘ └──────────────────┘
▲ │
│ Phoenix MCP, as the │
└────────────────────────┘
end user
- Browser → Phoenix. Your user is authenticated to Phoenix by an ordinary session. The chat request goes to a Phoenix API route scoped to your organization.
- Phoenix → OttoBot. Phoenix authorizes the request (§3), mints a short-lived credential bound to that user and organization, and proxies the turn to the OttoBot service over in-cluster networking. The OttoBot service has no public hostname and is not reachable from outside the cluster.
- OttoBot → Phoenix. To answer, OttoBot calls Phoenix's MCP data interface under the end user's identity, using a second short-lived credential. It cannot see data that the signed-in user could not already retrieve in Phoenix directly.
1.1 Shared multi-tenant deployment
Per-org OttoBot runs on a shared multi-tenant service. One process serves many customer organizations. Your organization does not receive a dedicated instance, and we do not currently offer a dedicated-instance tier.
Separation between organizations is logical and enforced per request: every turn resolves its own organization context from a validated credential, and the controls in §4 are applied on that per-request basis rather than being fixed when the service starts. §4.3 states the residual characteristic of this model honestly.
| Component | Provider | Region |
|---|---|---|
| Phoenix webapp | HG Insights (AWS EKS) | US |
| OttoBot service | HG Insights (AWS EKS) | US |
| Database | NeonDB (PostgreSQL) | US |
| Artifact & trace storage | AWS S3 | US |
| Model inference | Anthropic / OpenAI | Varies by provider |
2. Data flow and handling
2.1 What crosses each boundary
| Boundary | What crosses it |
|---|---|
| Browser → Phoenix | The user's message; optionally a client-held transcript of the current conversation (§2.2); the session cookie |
| Phoenix → OttoBot | The message and transcript; a short-lived credential identifying the user and organization |
| OttoBot → Phoenix MCP | Data queries issued as the end user, authenticated by a separate short-lived credential |
| OttoBot → model provider | The prompt, the persona/instructions for your organization, and tool results needed to answer |
2.2 Conversation history is not stored server-side
Per-org OttoBot chat has no server-side conversation history store. Turns are stateless on the server: there is no database table, cache, or file in which one organization's transcript is retained and could later be served to another.
Conversation context is instead supplied by the browser with each request, and is treated as untrusted input. It is validated against a strict schema that:
- rejects any unexpected field, at the top level or nested within a history entry;
- caps history at 50 messages per request;
- caps each individual message at 8,000 characters;
- rejects the request rather than silently truncating a malformed payload.
Because history is client-supplied, it is treated as conversational context, not as an authoritative record — and never as a source of authorization. What a user may retrieve is determined by their Phoenix identity (§3), not by anything asserted in the transcript.
2.3 What is persisted
| Data | Where | Notes |
|---|---|---|
| Operational traces | AWS S3 (US) | Written under a per-organization prefix derived per request (§4.1). Retention is bounded and configurable. |
| Generated artifacts | AWS S3 (US) | Same per-organization prefixing. |
| Chat transcripts | Not persisted server-side | See §2.2. |
Traces are written through a credential-redaction step that scrubs recognised secret formats — API keys, bearer and basic authorization headers, and credential-bearing connection strings — before anything is persisted. This is pattern-based redaction applied on the write path, not a guarantee that every possible secret shape is caught. For the platform-wide redaction behaviour applied to sensitive values, see Security Guardrails §2.
3. Authentication and authorization
3.1 Credential chain
A request is authorized in three stages, each narrower than the last:
- Phoenix session. The user authenticates to Phoenix normally. No OttoBot surface accepts anonymous traffic.
- Short-lived chat credential. Phoenix mints an opaque bearer token, scoped exactly to OttoBot chat, bound to the resolved user and organization, with a five-minute lifetime. It is minted server-side and forwarded to the OttoBot service; it is never used by the browser to call anything else.
- Short-lived data credential. When OttoBot needs to query Phoenix data, the chat credential is exchanged for a second, independently revocable token scoped to data access only, likewise bound to that user and organization.
The two credentials are not interchangeable in either direction: the chat credential is rejected by the data interface, and the data credential is rejected by the chat route. A token minted for one purpose cannot be replayed against the other surface, and neither carries a mixed or broadened scope — scope sets are matched exactly, not by prefix or subset.
3.2 Enforcement on the chat route
When your users chat with OttoBot inside Phoenix, every request passes the following checks before any turn begins. Each failure is terminal, and each is evaluated server-side on the request itself — none is inferred from the client.
| Check | Failure |
|---|---|
| Caller holds a valid Phoenix session | 401 |
| Caller is not a read-only administrator acting on behalf of the organization | 403 |
| Caller is a member of the organization named in the request path | 403 |
| The organization has OttoBot enabled, with a stored upstream that passes the egress allowlist (§3.3) | 404 / 422 |
Only after all of these pass does Phoenix mint the short-lived chat credential (§3.1) for that user and organization and begin the turn. Credential minting happens after authorization, not before it, so an unauthorized request never causes a credential to be issued.
Membership is checked on every request through Phoenix's authorization service. That service maintains a short-lived cache of membership results, so a membership change propagates within the cache lifetime rather than instantaneously.
The separate credential-bearing entry point used for embedded deployments applies additional checks — exact scope matching, organization binding on the credential, and revalidation of the underlying API key against the key registry. That surface is out of scope for this page (see Not covered here above).
3.3 Egress restrictions
Phoenix will only forward an OttoBot turn to an address that passes an allowlist check at the moment the route is configured:
- The OttoBot service address must be in-cluster service DNS, with one exception: a single hardcoded, non-routable hostname reserved for automated testing. Arbitrary public hostnames,
localhost, raw IP addresses, and embedded credentials are rejected. This prevents a misconfigured or maliciously edited route from redirecting a customer's conversation to an external endpoint. - The configuration store address must be either in-cluster service DNS or an HG-operated managed cache endpoint in our production region. The check is a naming-pattern match rather than an account-ownership proof, so it rejects a third party's endpoint in the same region on the strength of the hostname shape. Embedded credentials are likewise rejected.
Both checks are applied at write time, so an invalid destination cannot be stored in the first place.
4. Security controls
4.1 Per-request tenant controls
The following controls are live in production and applied per request, each deriving its behaviour from the validated credential's organization rather than from fixed start-up configuration. Each fails closed: an unrecognised organization is rejected rather than served a default.
| Control | What it enforces |
|---|---|
| Per-request persona selection | The instructions and workspace serving a turn are selected from the validated organization, behind an allowlist with path containment. An unknown organization is refused, not given a fallback persona. |
| Per-organization or disabled tool credentials | Third-party in-process integrations (for example Salesforce, Confluence) are disabled outright on the shared service, before any credential is read. Only per-user Phoenix data access is available, and that is already scoped to the requesting user. |
| Organization-scoped data session keying | The organization is part of the key identifying a pooled connection to Phoenix, so connections for two organizations cannot collide even if user, conversation, and thread identifiers were identical. Pooled connections are additionally torn down when the credential changes or expires. |
| Per-request trace and artifact prefixing | Trace paths, artifact paths, and observability tags are derived per request from the organization. Two organizations' traces and artifacts land under distinct prefixes and carry distinct tags. |
The browser chat surface accepts only these authenticated, organization-bound requests. There is no unauthenticated path into it, so there is no code path on which a turn could execute without a resolved organization.
4.2 Limits we do not overstate
- Spend caps are enforced at model provisioning, not in-process. The in-process budget guard operates per turn: it stops a single runaway conversation. It is not a cumulative per-organization quota. Caps therefore bound spend; they are not a concurrency control, and we do not claim otherwise.
- The model-facing protections for OttoBot are prompt-level. They live in the persona and instruction layer. Per-org OttoBot chat does not run a pre-model classifier or input-scanning filter in front of the model. The input sanitization described in Security Guardrails §1 applies to the Phoenix agent product, not to this path. The redaction (§2), output guardrails (§3), and adversarial testing programme (§4) described there are platform-wide.
- Authorization is not delegated to the model. What a user can retrieve is fixed by the credential chain in §3 before the model is invoked. Prompt content — including client-supplied history — cannot widen it, because the data credential is bound to the validated user and organization at mint time and cannot be swapped by anything the model emits.
4.3 Residual risk of a shared service
A shared service means one process memory space. Organizations served concurrently by the same process are separated by the per-request controls above rather than by an operating-system or hardware boundary. A sufficiently severe defect in the runtime — one permitting arbitrary code execution or out-of-bounds memory disclosure — is the class of issue those controls narrow but do not categorically eliminate.
We state this plainly because it is the honest characterization of the deployment model, and because we do not currently offer a dedicated-instance alternative. The controls in §4.1 exist to minimize what is resident per turn: credentials are short-lived and per-request, third-party integration credentials are not loaded at all, and no organization's conversation is retained server-side between turns.
Organizations whose requirements do not permit a shared process should raise this with their HG Insights representative before enabling OttoBot.
5. Certifications and attestations
Phoenix is operated by HG Insights and is covered by HG Insights' corporate security programme.
For SOC 2 reports, penetration-testing summaries, and other formal attestations, contact HG Insights Corporate InfoSec through your HG Insights representative. Those artifacts are issued and maintained at the corporate level.
This page makes no independent certification claim on behalf of the OttoBot integration. It describes architecture and controls; it is not itself an attestation, and it does not assert that this integration has been separately certified.
| Question | Where to go |
|---|---|
| SOC 2, penetration tests, formal attestations | HG Insights Corporate InfoSec |
| Contractual terms, audit rights, liability | Your HG Insights MSA |
| Data-residency requirements beyond US | Your HG Insights representative |
| Technical questions about this page | Your HG Insights representative |
Document control
| Version | Date | Author | Changes |
|---|---|---|---|
| 0.1 | 2026-08-21 | Phoenix Team | Initial publication — per-org OttoBot architecture, data flow, authentication, tenant-isolation controls, and residual-risk disclosure for the shared multi-tenant deployment |