Skill Vault
Quick example
- curl
- Python
- Node.js
# Ingest a skill manifest, then trigger a scan
curl "$TRINITITE_BASE/v1/skills" \
-H "Authorization: Bearer $TRINITITE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "tableau-summarizer",
"manifest_url": "https://example.com/skills/tableau-summarizer.md"
}'
curl "$TRINITITE_BASE/v1/skills/$SKILL_ID/scan" \
-H "Authorization: Bearer $TRINITITE_API_KEY" \
-X POST
import os, requests
api = f"{os.environ['TRINITITE_BASE']}"
hdr = {"Authorization": f"Bearer {os.environ['TRINITITE_API_KEY']}"}
skill = requests.post(f"{api}/v1/skills", headers=hdr, json={
"name": "tableau-summarizer",
"manifest_url": "https://example.com/skills/tableau-summarizer.md"
}).json()
requests.post(f"{api}/v1/skills/{skill['id']}/scan", headers=hdr)
const api = process.env.TRINITITE_BASE;
const hdr = { Authorization: `Bearer ${process.env.TRINITITE_API_KEY}`, 'Content-Type': 'application/json' };
const skill = await (await fetch(`${api}/v1/skills`, {
method: 'POST', headers: hdr,
body: JSON.stringify({ name: 'tableau-summarizer', manifest_url: 'https://example.com/skills/tableau-summarizer.md' }),
})).json();
await fetch(`${api}/v1/skills/${skill.id}/scan`, { method: 'POST', headers: hdr });
See Skill Vault for the architecture.
Overview
The Skill Vault is a cryptographic registry, scanner, and signer for SKILL.md files that AI agents load into their progressive-disclosure context. Before any enterprise agent loads a third-party skill, the vault:
- Ingests the SKILL.md content and computes a SHA-256 content hash.
- Scans for malicious patterns: curl-pipes, base64 obfuscation, reverse shells, credential access, env exfiltration, external-IP access, privilege escalation, blocked domains.
- Quarantines suspicious or malicious skills.
- Signs clean skills with a cryptographic signature tied to the approver's identity.
- Tracks the full provenance chain in an immutable audit trail.
The result is an Agentic Software Bill of Materials: every skill, content-hashed, scanned, classified, and signed. Signatures are verifiable via the platform's public JWKS bundle — see Public Verification.
Authentication: Authorization: Bearer <session_token | api_key> with the relevant skills:* permission.
Endpoints
| Method | Path | Permission |
|---|---|---|
GET | /v1/skills | skills:read |
POST | /v1/skills | skills:create |
GET | /v1/skills/{id} | skills:read |
POST | /v1/skills/{id}/scan | skills:create |
POST | /v1/skills/{id}/approve | skills:update |
POST | /v1/skills/{id}/revoke | skills:update |
GET | /v1/skills/{id}/scans | skills:read |
GET | /v1/skills/{id}/scans/{scanId} | skills:read |
GET | /v1/skills/scan-rules | skills:read |
POST | /v1/skills/scan-rules | skills:create |
PATCH | /v1/skills/scan-rules/{id} | skills:update |
DELETE | /v1/skills/scan-rules/{id} | skills:delete |
Lifecycle
pending_scan → scanning → approved (signed)
↘ quarantined → POST /approve (manual override) → approved
approved → revoked
approved → POST /scan (re-scan) → scanning
| Status | Meaning |
|---|---|
pending_scan | Ingested, awaiting scan |
scanning | Scan in progress |
quarantined | Scan found suspicious or malicious content |
approved | Clean scan or manual approval; skill is signed |
revoked | Previously approved; revoked due to advisory |
Scan verdicts
| Verdict | Meaning | Auto-action |
|---|---|---|
clean | No findings | Auto-approved (signature applied) |
suspicious | Medium / high findings, no critical | Auto-quarantined |
malicious | Critical findings (curl pipe, reverse shell, etc.) | Auto-quarantined |
Per-scan booleans surface specific pattern hits: has_curl_pipe, has_base64_obfuscation, has_external_ip_access, has_env_exfiltration, has_credential_access, has_reverse_shell, has_privilege_escalation, plus blocked_domains[].
Scan findings include the pattern name, count, severity, and a free-text detail — the actual malicious content is never returned, only signatures and counts. This is a deliberate defence against scanner abuse.
POST /v1/skills
Ingest a new SKILL.md.
{
"name": "Kubernetes Deployer",
"description": "Deploys YAML manifests to a target cluster.",
"source_url": "https://github.com/example/skills/raw/main/k8s-deployer/SKILL.md",
"source_hub": "github",
"content": "---\ntools_required: [kubectl]\nplatforms: [linux, darwin]\n---\n\n## Usage\n..."
}
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Display name |
description | string | No | Free-form description |
source_url | string | No | Origin URL (e.g. GitHub raw URL) |
source_hub | string | No | cursor, github, community, or any custom value |
content | string | Yes | Full SKILL.md content |
Response — 201 Created:
{
"skill_id": "sk_01JF8RSK1A2B3C4D5E6F7G8H9I",
"name": "Kubernetes Deployer",
"status": "pending_scan",
"content_hash": "sha256:e3b0c44298fc1c149afbf4c8996fb924...",
"estimated_tokens": 512,
"tools_required": ["kubectl"],
"platforms": ["linux", "darwin"],
"created_at": "2026-05-01T22:14:00Z"
}
The platform parses YAML frontmatter and populates tools_required[], platforms[], and yaml_metadata automatically when present.
GET /v1/skills
| Query | Type | Description |
|---|---|---|
status | string | pending_scan, scanning, quarantined, approved, revoked |
source_hub | string | Filter by source hub |
limit | integer | Default 100, max 1000 |
offset | integer | — |
{
"total": 42,
"skills": [
{
"skill_id": "sk_01JF8RSK1A2B3C4D5E6F7G8H9I",
"name": "Kubernetes Deployer",
"status": "approved",
"content_hash": "sha256:e3b0c44...",
"source_hub": "github",
"signature": "ed25519:...",
"signed_by": "usr_01JF8RUS1A2B3C4D5E6F7G8H9I",
"signed_at": "2026-05-01T22:15:00Z"
}
]
}
GET /v1/skills/{id}
Returns a single skill with current status, scan summary, signature (if approved), and YAML metadata.
POST /v1/skills/{id}/scan
Trigger a (re-)scan. Requires the content to be submitted — producing a scan when the content has changed creates a new scan-result row, and the skill's content_hash updates.
{
"content": "...latest SKILL.md text...",
"scanner_version": "v2.4.0"
}
Returns a scan result.
POST /v1/skills/{id}/approve
Manually approve a quarantined skill. The platform signs the skill with the approver's identity (Ed25519). Once signed, the signature appears in subsequent GET responses and is verifiable via the JWKS endpoint.
{
"skill_id": "sk_01JF8RSK1A2B3C4D5E6F7G8H9I",
"status": "approved",
"signature": "ed25519:e3b0c44298fc1c149afbf4c8996fb924...",
"signed_by": "usr_01JF8RUS1A2B3C4D5E6F7G8H9I",
"signed_at": "2026-05-01T22:32:00Z"
}
Returns 409 conflict if the skill is already approved.
POST /v1/skills/{id}/revoke
Revoke a previously approved skill — useful when a downstream advisory invalidates a known-good version.
{ "reason": "CVE-2026-XXXX disclosed against tools_required:kubectl < 1.30." }
Scan results
GET /v1/skills/{id}/scans
WORM history of every scan. Cursor pagination.
{
"data": [
{
"scan_id": "scn_01JF8RSCN1A2B3C4D5E6F7G8H9I",
"skill_id": "sk_01JF8RSK1A2B3C4D5E6F7G8H9I",
"scanner_version": "v2.4.0",
"verdict": "clean",
"risk_score": 0.04,
"findings": [],
"has_curl_pipe": false,
"has_base64_obfuscation": false,
"has_external_ip_access": false,
"has_env_exfiltration": false,
"has_credential_access": false,
"has_reverse_shell": false,
"has_privilege_escalation": false,
"blocked_domains": [],
"scanned_at": "2026-05-01T22:15:00Z"
}
],
"page": { "next_cursor": null, "has_more": false }
}
GET /v1/skills/{id}/scans/{scanId}
Returns one scan result, including the full findings array (each: pattern, count, severity, detail).
Scan rules
Custom organisation-level detection rules that extend the built-in pattern library.
{
"rule_type": "domain_blocklist",
"name": "no-paste-services",
"patterns": ["pastebin.com", "ix.io", "0x0.st"],
"severity": "high",
"enabled": true
}
| Field | Type | Required | Description |
|---|---|---|---|
rule_type | enum | Yes | pattern_match, domain_blocklist, domain_allowlist, executable_blocklist, semantic |
name | string | Yes | Unique within rule type |
patterns | string[] | Yes | Strings, glob patterns, or regex (per rule_type) |
severity | string | Yes | low, medium, high, critical |
enabled | boolean | No | Default true |
GET, POST, PATCH, DELETE follow the standard CRUD shape on /v1/skills/scan-rules[/...].
Errors
| HTTP | error.code | Cause |
|---|---|---|
400 | validation_error | Body or query failed schema validation |
401 | unauthenticated | Missing or invalid credential |
403 | forbidden | Caller lacks the required skills:* permission |
404 | not_found | Skill, scan, or rule not found |
409 | conflict | Skill already in target status (e.g. approve while approved) |
429 | rate_limited | Per-organization rate limit exceeded |
Next steps
- Verify a skill signature with no platform credential → Public Verification
- Govern shell commands the skill might run → CLI Firewall
- Track per-skill loads in agent activity → Logs API