Security
Secret redaction at output boundaries, path traversal defense, TOCTOU primitives.
Canonical contract:
packages/sdk/docs.md— "Security — secret redaction" + "Security — path traversal + TOCTOU".
The SDK takes two security concerns seriously: secrets in observable output and filesystem operations on untrusted input.
Secret redaction (ADR D68-D73)
Every output boundary (logs, telemetry attrs, error metadata, transcripts) is wrapped by redactSecrets. The canonical implementation lives at internal/security/redact.ts — there's one source of truth.
What gets redacted:
- API key patterns (
sk-...,Bearer ey...,xoxb-...) - AWS access keys (
AKIA[0-9A-Z]{16}) - GitHub tokens (
ghp_...,gho_...) - JWT-like tokens (3-part base64url)
- URL
?token=/?api_key=query params - Environment-variable values when stringified
Two-bucket masking (ADR D71):
- Short tokens (less than 18 chars) →
*** - Long tokens →
prefix...suffix(first 4 + last 4)
Opt-out (NOT recommended):
export THEOKIT_REDACT_SECRETS=0Emits a one-time stderr warning (ADR D70). The env var is snapshotted at module init (ADR D69) — prompt-injection defense.
codeFile: true flag (ADR D72):
When generating .env.example placeholders or similar, set codeFile: true to skip PARAM_PATTERN redaction — preserves intentional examples like OPENAI_API_KEY=sk-....
Path traversal + TOCTOU (ADRs D79-D85)
When the SDK writes to user-supplied paths (skills, agents, personalities, OAuth tokens), it routes through path-safety primitives:
| Primitive | Use |
|---|---|
safePathJoin(base, ...parts) | Resolves THEN prefix-checks (defeats normalized .. escape) |
sanitizeIdentifier(name) | Strict grammar ^[a-z0-9][a-z0-9_-]*$ |
createExclusive(path, mode) | O_EXCL open with default mode 0o600 |
casUpdate(db, stmt, params) | SQLite optimistic compare-and-swap |
Available as Security.* namespace and at @theokit/sdk/path-safety sub-export (separate from main barrel to keep DTS bundle clean).
import { Security } from "@theokit/sdk";
const safe = Security.safePathJoin(".theokit/skills", userProvidedName);CI lint gate (ADR D85)
packages/sdk/tests/lint/no-unredacted-sink.test.ts runs in CI and fails if a new console.log/writeFile/span.setAttribute lands in src/ without going through redactSecrets. Each whitelisted file carries a rationale comment.
packages/sdk/tests/lint/no-todo-fixme.test.ts prevents production stubs.