Harbor - AI Governance
Harbor is the AI governance layer (powered by CortexDB) built into Dooor OS. Every app you deploy gets full observability, guardrails and an immutable audit trail for free - no SDK setup, no extra config. You just read two env vars that are already in your container.
How it works (zero-config)
Harbor is provisioned automatically the first time your app is deployed. The platform creates a private CortexDB database, mints an API key, and injects two environment variables into your container:
| Name | Type | Required | Description |
|---|---|---|---|
| CORTEXDB_CONNECTION | string | optional | Full connection string in the form cortexdb://<api_key>@<host>/<database>. This is the credential your app code uses to talk to Harbor. |
| HARBOR_PROJECT | string | optional | The App ID, used by the Dooor AI toolkit and the Harbor proxy to tag every trace, eval and guard block to your app. |
Lifecycle
git push # you push code
-> Build (Kaniko / Cloud Build)
-> Deployment event fires
-> Harbor listener auto-provisions:
* creates CortexDB database
* mints scoped API key
* sets CORTEXDB_CONNECTION + HARBOR_PROJECT
-> Your container starts with both env vars already set
-> Every AI call you make is traced, evaluated and guard-checked.Use Harbor in your app
Pick the path that matches your stack. Both ship identical traces, evals and guard blocks to Harbor.
- LangChain users —
createAutoLlmwraps any LangChain chat model with one line. Reads CORTEXDB_CONNECTION and HARBOR_PROJECT from env, pulls the per-database guards/evals config from Harbor on boot, and instruments every.invoke()/.stream(). This is the only truly zero-config path. - Everyone else (OpenAI SDK, Anthropic SDK, Gemini SDK, raw HTTP) —
configureCortexDBFromConnectionStringonce at boot (still reads the env var), then calllogTrace(...)after each LLM call. Two lines of plumbing, then a normal trace object per call.
// Not using LangChain (OpenAI SDK, Anthropic SDK, Gemini SDK, raw HTTP)?
// npm install @dooor-ai/toolkit
//
// configureCortexDBFromConnectionString once at boot (reads the env var).
// Then wrap each LLM call with logTrace. No proxy URL to remember —
// the toolkit handles routing to the right CortexDB database.
import {
configureCortexDBFromConnectionString,
logTrace,
} from "@dooor-ai/toolkit"
import OpenAI from "openai"
configureCortexDBFromConnectionString(process.env.CORTEXDB_CONNECTION!)
const openai = new OpenAI()
const start = Date.now()
const res = await openai.chat.completions.create({
model: "gpt-4o-mini",
messages: [{ role: "user", content: "Hello" }],
})
await logTrace(
{
traceId: crypto.randomUUID(),
input: "Hello",
output: res.choices[0].message.content ?? "",
model: "gpt-4o-mini",
latency: Date.now() - start,
tokens: {
prompt: res.usage?.prompt_tokens ?? 0,
completion: res.usage?.completion_tokens ?? 0,
total: res.usage?.total_tokens ?? 0,
},
timestamp: new Date(),
},
{ project: process.env.HARBOR_PROJECT },
)What you get for free
Every call routed through Harbor is automatically:
- Traced - request, response, model, tokens, latency, cost - queryable via the Harbor dashboard or the Traces API below.
- Evaluated - relevance, safety, policy compliance scores attached to the trace.
- Guard-checked - PII detection, prompt injection, toxicity and custom guards run pre and post-call. Blocked calls show up under Guard Blocks.
- Audit-logged - immutable trail of every prompt, response and guard decision, scoped to your workspace.
Programmatic access
The endpoints below are for dashboards, internal tooling and admin scripts - not for the request path of your app. Use a workspace API key (dor_sk_*). App-scoped Harbor endpoints live under /workspaces/{workspaceId}/harbor/apps/{appId}/. Global guard-template endpoints live under /harbor/guard-templates.
Analytics Summary
/workspaces/{workspaceId}/harbor/apps/{appId}/analytics/summaryReturns aggregate analytics for all AI calls made through Harbor for a given time range.
Query Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| start | string (ISO 8601) | optional | Start of the time range. Default: 7 days ago. |
| end | string (ISO 8601) | optional | End of the time range. Default: now. |
Response
{
"appId": "app_01hx...",
"period": {
"start": "2025-01-13T00:00:00Z",
"end": "2025-01-20T00:00:00Z"
},
"totalCalls": 1842,
"totalTokens": 4250000,
"avgLatencyMs": 620,
"totalCostUsd": 12.75,
"blockedByGuard": 14,
"errorRate": 0.008
}curl -X GET "https://os-develop.dooor.ai/api/v1/workspaces/{workspaceId}/harbor/apps/{appId}/analytics/summary?start=2025-01-13T00:00:00Z&end=2025-01-20T00:00:00Z" \
-H "Authorization: Bearer dor_sk_your_key_here"Analytics Timeseries
/workspaces/{workspaceId}/harbor/apps/{appId}/analytics/timeseriesReturns a timeseries for a specific metric, bucketed by the given interval. Useful for rendering charts.
Query Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| metric | string | optional | Metric name: total_calls | total_tokens | avg_latency | total_cost | error_rate. |
| start | string (ISO 8601) | optional | Start of the time range. |
| end | string (ISO 8601) | optional | End of the time range. |
| interval | string | optional | Bucket size: 1h | 6h | 1d | 7d. Default: 1h. |
Response
{
"metric": "total_calls",
"interval": "1h",
"labels": [
"2025-01-20T00:00:00Z",
"2025-01-20T01:00:00Z",
"2025-01-20T02:00:00Z"
],
"values": [84, 112, 97]
}curl -X GET "https://os-develop.dooor.ai/api/v1/workspaces/{workspaceId}/harbor/apps/{appId}/analytics/timeseries?metric=total_calls&interval=1h&start=2025-01-20T00:00:00Z&end=2025-01-20T23:59:59Z" \
-H "Authorization: Bearer dor_sk_your_key_here"Token Usage
/workspaces/{workspaceId}/harbor/apps/{appId}/analytics/token-usageReturns a breakdown of prompt, completion, and total token consumption for the given time range.
Query Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| start | string (ISO 8601) | optional | Start of the time range. |
| end | string (ISO 8601) | optional | End of the time range. |
Response
{
"promptTokens": 2800000,
"completionTokens": 1450000,
"totalTokens": 4250000
}curl -X GET "https://os-develop.dooor.ai/api/v1/workspaces/{workspaceId}/harbor/apps/{appId}/analytics/token-usage?start=2025-01-13T00:00:00Z&end=2025-01-20T00:00:00Z" \
-H "Authorization: Bearer dor_sk_your_key_here"Eval Score Distribution
/workspaces/{workspaceId}/harbor/apps/{appId}/analytics/eval-distributionReturns the score distribution across all evals run for an app in the given time range. Useful for histograms of latency/quality scores.
Query Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| start | string (ISO 8601) | optional | Start of the time range. |
| end | string (ISO 8601) | optional | End of the time range. |
curl -X GET "https://os-develop.dooor.ai/api/v1/workspaces/{workspaceId}/harbor/apps/{appId}/analytics/eval-distribution?start=2025-01-13T00:00:00Z&end=2025-01-20T00:00:00Z" \
-H "Authorization: Bearer dor_sk_your_key_here"List Traces
/workspaces/{workspaceId}/harbor/apps/{appId}/tracesReturns a paginated list of AI traces recorded by Harbor for the app. Each trace represents a single AI call with full request and response detail, latency, token usage, and guard evaluation results.
Query Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| limit | integer | optional | Traces per page. Default: 20. |
| offset | integer | optional | Offset for pagination. Default: 0. |
| project | string | optional | Filter by CortexDB project name. |
| session_id | string | optional | Filter traces by session ID. |
Response
{
"data": [
{
"traceId": "trc_01hx...",
"sessionId": "sess_01hx...",
"model": "gemini-3-flash-preview",
"promptTokens": 320,
"completionTokens": 148,
"totalTokens": 468,
"latencyMs": 512,
"guardBlocked": false,
"evalScore": 0.92,
"createdAt": "2025-01-20T09:05:30Z"
}
],
"total": 1842,
"limit": 20,
"offset": 0
}curl -X GET "https://os-develop.dooor.ai/api/v1/workspaces/{workspaceId}/harbor/apps/{appId}/traces?limit=20&offset=0" \
-H "Authorization: Bearer dor_sk_your_key_here"Get Trace
/workspaces/{workspaceId}/harbor/apps/{appId}/traces/{traceId}Returns the full detail for a single trace, including the raw prompt, response, guard evaluation breakdown, and any eval scores.
Response
{
"traceId": "trc_01hx...",
"sessionId": "sess_01hx...",
"model": "gemini-3-flash-preview",
"prompt": "Summarize the following contract clause...",
"response": "The clause states that...",
"promptTokens": 320,
"completionTokens": 148,
"totalTokens": 468,
"latencyMs": 512,
"guardBlocked": false,
"guardEvaluations": [
{
"guardType": "pii_detection",
"result": "pass",
"confidence": 0.99
}
],
"evalScore": 0.92,
"createdAt": "2025-01-20T09:05:30Z"
}curl -X GET "https://os-develop.dooor.ai/api/v1/workspaces/{workspaceId}/harbor/apps/{appId}/traces/{traceId}" \
-H "Authorization: Bearer dor_sk_your_key_here"List Evals
/workspaces/{workspaceId}/harbor/apps/{appId}/evalsReturns evaluation results attached to AI traces. Evals measure output quality, safety, and policy compliance using automated scoring.
Query Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| limit | integer | optional | Evaluations per page. Default: 20. |
| offset | integer | optional | Offset for pagination. Default: 0. |
| trace_id | string | optional | Filter evals for a specific trace. |
Response
[
{
"id": "eval_01hx...",
"traceId": "trc_01hx...",
"evalType": "relevance",
"score": 0.92,
"passed": true,
"details": "Response is highly relevant to the user prompt.",
"createdAt": "2025-01-20T09:05:31Z"
}
]curl -X GET "https://os-develop.dooor.ai/api/v1/workspaces/{workspaceId}/harbor/apps/{appId}/evals?limit=20" \
-H "Authorization: Bearer dor_sk_your_key_here"List Guard Blocks
/workspaces/{workspaceId}/harbor/apps/{appId}/guard-blocksReturns a list of guard block instances - AI calls that were intercepted and blocked by a Harbor guard rule. Each record includes the guard type that triggered the block and the matched content.
Query Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| limit | integer | optional | Records per page. Default: 20. |
| offset | integer | optional | Offset for pagination. Default: 0. |
| guard_type | string | optional | Filter by guard type: pii_detection | prompt_injection | toxicity | off_topic. |
Response
[
{
"id": "blk_01hx...",
"traceId": "trc_01hx...",
"guardType": "pii_detection",
"blockedContent": "User sent a message containing a credit card number",
"confidence": 0.98,
"action": "block",
"createdAt": "2025-01-20T10:15:00Z"
}
]curl -X GET "https://os-develop.dooor.ai/api/v1/workspaces/{workspaceId}/harbor/apps/{appId}/guard-blocks?guard_type=pii_detection" \
-H "Authorization: Bearer dor_sk_your_key_here"Guard Templates
Guard templates define reusable guard rule configurations that can be applied to any Harbor-provisioned app. Manage templates independently of apps using the following endpoints.
/harbor/guard-templatesReturns all guard templates available in the platform.
Response
[
{
"id": "gtpl_01hx...",
"name": "PII Shield",
"description": "Blocks prompts and responses containing personally identifiable information",
"guardType": "pii_detection",
"config": {
"sensitivity": "high",
"action": "block"
},
"createdAt": "2025-01-10T08:00:00Z"
}
]curl -X GET "https://os-develop.dooor.ai/api/v1/harbor/guard-templates" \
-H "Authorization: Bearer dor_sk_your_key_here"/harbor/guard-templatesCreates a new guard template.
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | required | Human-readable name for the template. |
| description | string | optional | Description of what the guard does. |
| guardType | string | required | Guard type: pii_detection | prompt_injection | toxicity | off_topic | custom. |
| config | object | optional | Guard-specific configuration (sensitivity, action, allowlist, etc.). |
curl -X POST "https://os-develop.dooor.ai/api/v1/harbor/guard-templates" \
-H "Authorization: Bearer dor_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"name": "PII Shield",
"description": "Blocks PII in prompts and responses",
"guardType": "pii_detection",
"config": { "sensitivity": "high", "action": "block" }
}'/harbor/guard-templates/{id}Fully replaces a guard template. All fields must be provided.
curl -X PUT "https://os-develop.dooor.ai/api/v1/harbor/guard-templates/{id}" \
-H "Authorization: Bearer dor_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"name": "PII Shield v2",
"description": "Updated PII detection with medium sensitivity",
"guardType": "pii_detection",
"config": { "sensitivity": "medium", "action": "redact" }
}'/harbor/guard-templates/{id}Permanently deletes a guard template. Apps currently using the template will retain the last applied configuration until re-configured. Returns 204 No Content.
curl -X DELETE "https://os-develop.dooor.ai/api/v1/harbor/guard-templates/{id}" \
-H "Authorization: Bearer dor_sk_your_key_here"
# Returns 204 No ContentObservability Config
Per-app guards/evals configuration. The Dooor AI toolkit fetches this at boot to materialize guards and evals — so any change applies on the next Pod restart.
/workspaces/{workspaceId}/harbor/apps/{appId}/observability-configReturns the current guards + evals config for the app.
Response
{
"guards": {
"prompt_injection": { "enabled": true, "threshold": 0.8 },
"sensitive_data": { "enabled": true, "threshold": 0.7 },
"custom_templates": [
{
"template_id": "gtpl_01hx...",
"template_name": "PII Shield",
"enabled": true,
"threshold": 0.7
}
]
},
"evals": {
"latency": { "enabled": true, "threshold_ms": 3000, "target_ms": 800, "max_ms": 5000 }
}
}curl -X GET "https://os-develop.dooor.ai/api/v1/workspaces/{workspaceId}/harbor/apps/{appId}/observability-config" \
-H "Authorization: Bearer dor_sk_your_key_here"/workspaces/{workspaceId}/harbor/apps/{appId}/observability-configPartial update. Send only the fields you want to change. Both top-level keys (guards, evals) are optional, as is every nested guard/eval.
Request Body
| Name | Type | Required | Description |
|---|---|---|---|
| guards | object | optional | Optional. Each guard accepts { enabled?: boolean, threshold?: number }. Recognised keys: prompt_injection, sensitive_data. To enable custom guard templates, pass guards.custom_templates as an array of { template_id, template_name, enabled, threshold }. |
| evals | object | optional | Optional. evals.latency accepts { enabled?, threshold_ms?, target_ms?, max_ms? }. |
curl -X PUT "https://os-develop.dooor.ai/api/v1/workspaces/{workspaceId}/harbor/apps/{appId}/observability-config" \
-H "Authorization: Bearer dor_sk_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"guards": {
"prompt_injection": { "enabled": true, "threshold": 0.9 }
},
"evals": {
"latency": { "threshold_ms": 2000 }
}
}'Re-provision Harbor (rare)
/workspaces/{workspaceId}/harbor/apps/{appId}/provisionHarbor is provisioned automatically on the first deploy of every app, so you should rarely need this. Call this endpoint only to force a fresh CortexDB database for an app that lost its CORTEXDB_CONNECTION env var or to recover from a partially failed deploy.
Response
{
"appId": "app_01hx...",
"harborDatabase": "doos_ws01_my_app",
"connectionString": "cortexdb://k_***@harbor.dooor.ai/doos_ws01_my_app"
}curl -X POST "https://os-develop.dooor.ai/api/v1/workspaces/{workspaceId}/harbor/apps/{appId}/provision" \
-H "Authorization: Bearer dor_sk_your_key_here"De-provision Harbor
/workspaces/{workspaceId}/harbor/apps/{appId}/provisionRemoves the Harbor integration for an app. The CortexDB project and all associated traces, evals, and guard blocks are permanently deleted. Returns 204 No Content on success.
curl -X DELETE "https://os-develop.dooor.ai/api/v1/workspaces/{workspaceId}/harbor/apps/{appId}/provision" \
-H "Authorization: Bearer dor_sk_your_key_here"
# Returns 204 No Content