Celon

Governance

Governance

Built for the regulatory demands of Korean enterprises — prompt bodies are never stored, personal data is masked before it leaves the gateway, and both spend and data are governed through three budget tiers (organization, team/department, key) plus organization-specific guardrails (custom PII patterns, banned words, model policy). Configure it through the admin API or the governance console in the dashboard.

ZDR — Zero Data Retention

The gateway never stores prompt or response bodies. The usage ledger records metadata only:

  • ts · keyId · orgId · endpoint
  • model · provider · tier
  • promptTokens · completionTokens · costUsd · savedUsd
  • cacheHit · latencyMs · status

Internal debug logging follows the same policy — the content, messages, input, and prompt fields are replaced with "[ZDR]" right before anything is logged. The observability integration (Langfuse) also sends metadata only.

PII masking

When piiMasking is enabled on a key, the five categories of personal data below are replaced with per-type tokens before the request leaves for the upstream provider.

TypePatternReplacement token
Resident registration number (RRN)YYMMDD-NNNNNNN — with date validation[MASKED:RRN]
Credit card13–19 digits (spaces/dashes allowed), only when the Luhn checksum passes[MASKED:CARD]
Mobile phone01[016789]-XXXX-XXXX, including the +82 international format[MASKED:PHONE]
EmailStandard email format[MASKED:EMAIL]
Passport numberM/S + 8 digits[MASKED:PASSPORT]
example
Input: My number is 010-1234-5678 and my email is kim@acme.co.kr
Sent:  My number is [MASKED:PHONE] and my email is [MASKED:EMAIL]
  • The masking order is fixed as RRN → CARD → PHONE → EMAIL → PASSPORT to prevent false positives (for example, a phone-number-shaped run inside a card number).
  • Per-key on/off — controlled by the piiMasking flag on the key record, and can be turned off per request with celon.no_pii_mask.
  • The number of masked items is reported in the pii_masked response metadata.
  • Masking is one-way — the gateway keeps no mapping for restoring the original text (ZDR).

Organization-specific patterns and banned words

Beyond the five defaults, an organization can register its own policies under governance.guardrails on the org record. When guardrails is set on a key, the key takes precedence (key override → organization policy → gateway default).

FieldDetails
pii.entitiesIndividual on/off for the five defaults — e.g. { "EMAIL": false } excludes only email from masking
pii.customCustom regex patterns (up to 64) — { "name": "EMPLOYEE_ID", "pattern": "EMP-\\d{6}" }. Matched spans are replaced with [MASKED:EMPLOYEE_ID]. Invalid or pathologically slow patterns are rejected with 400 at registration time.
bannedWordsA banned-word list (up to 500, case-insensitive substring match) — each entry follows the policy mode by default, and its action can individually set block/mask/log-only. An action: "block" entry always blocks even when the policy is mask/shadow.

Model policy

Restrict which models can be used per organization or key — register blockedModels/allowedModels under the org's governance.models or the key's modelPolicy (the key wins, blocks win over allows, and an empty allow list permits everything). Entries accept catalog model ids, namespace wildcards like openai/*, and celon virtual ids.

  • Directly requesting a blocked model is rejected with 403 (model_blocked) — this applies across every surface: chat, embeddings, images, and audio.
  • celon/auto routing drops blocked models from the candidate set and routes around them to allowed models — if an entire tier is blocked, it escalates to the tier above.
  • GET /v1/models returns only the list reflecting the key's effective policy — blocked models do not appear in the catalog.

Tiered guardrail stack

The PII masking above is stage 1 of the guardrail stack. Before a request leaves for the upstream, it passes through the detectors below in fastest-first order — the cheap regex stages catch most of it, and the expensive stages run only when needed. Per-detector on/off is controlled by the key's guardrails.detectors policy (see the API reference).

StageDetectorLatencyDetails
1Structured PII detectors.piisub-msRegex — RRN, card (Luhn), phone, email, passport (same as the table above)
2Secrets / entropy detectors.secrets~1–2msAPI-key and cloud-token patterns plus high-entropy strings (leaked credentials)
3Presidio NER detectors.presidioexternal callUnstructured PII (names, addresses, organizations) — an external Presidio instance, enabled only when PRESIDIO_URL is set
4Prompt-injection heuristics detectors.injectionsub-msScreens for jailbreak and instruction-override patterns — the integration point for Prompt Guard 2

The three enforcement modes

Separate from what gets detected, how a detection is handled is set by the key's guardrails.mode.

ModeNameBehavior
blockValidationRejects the request immediately on a violation — only a summary of the violated categories is returned, never the body.
maskMutationAutomatically masks the detected spans with [MASKED:*] tokens, then forwards to the upstream (one-way, not reversible).
shadowShadowPasses the request through unchanged and only records the detections (audit only) to the Audit Trail — ideal for a dry run before rolling out a policy.

In any mode, the per-request result is reported in the guardrails.findings response metadata and the x-celon-guardrail-findings header — it carries only counts and categories, never the original text (ZDR).

Streaming output guardrails

When guardrails.streamOutput is enabled on a key, the model's output is inspected too — SSE chunks are collected in a sliding buffer and validated incrementally, so even a resident registration number or secret split across token boundaries is caught. On a confirmed violation, the stream is halted immediately with finish_reason: "content_filter".

Audit Trail

Every guardrail decision is recorded to an immutable, append-only metadata log — only the mode, per-category detection counts, whether it was blocked, and the request id are stored; prompt and response bodies are not (ZDR). Query it with GET /admin/audit (see the API reference).

BYOK — Bring Your Own Key

Use the upstream contracts your organization already holds (OpenAI, Anthropic, and so on) as-is. Register per-provider keys under byokKeys on the key record, and calls to that provider use your organization's key instead of the platform's shared key — this applies to both chat and embeddings.

Register them through the admin API (master key required; see the API reference for the full schema):

terminal
curl -X POST https://api.celon.ai/admin/keys \
  -H "Authorization: Bearer $CELON_MASTER_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "orgId": "org_abc123",
    "name": "prod-backend",
    "piiMasking": true,
    "zdr": true,
    "byokKeys": {
      "openai": "sk-proj-…",
      "anthropic": "sk-ant-…"
    }
  }'

Budget policy — organization, team (department), key

Budgets are tallied by UTC calendar month and enforced at three levels: organization → team (department) → key. null means unlimited. A key joins a team via teamId, and team spend is recorded against its membership at request time — moving a key to another team leaves past spend with the original team.

ScopeBehavior when exceeded
Organization budgetA hard cap — always rejected with 402 (budget_exceeded). It takes precedence regardless of the team and key policies.
Team (department) budgetFollows the team's overBudgetBehavior "reject" denies with 402, "downgrade" forces a switch to Tier 3. When the team and key both exceed at once, the stricter decision wins.
Key budgetFollows the key's overBudgetBehavior "reject" denies with 402, "downgrade" forces celon/auto requests to Tier 3 to keep serving at low cost.
  • downgrade applies only to celon/auto — a request that names a specific model keeps that model.
  • Embeddings have no downgrade concept — exceeding the budget only ever rejects with 402.