Skip to main content

Skill Vault

Quick example

# 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

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:

  1. Ingests the SKILL.md content and computes a SHA-256 content hash.
  2. Scans for malicious patterns: curl-pipes, base64 obfuscation, reverse shells, credential access, env exfiltration, external-IP access, privilege escalation, blocked domains.
  3. Quarantines suspicious or malicious skills.
  4. Signs clean skills with a cryptographic signature tied to the approver's identity.
  5. 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

MethodPathPermission
GET/v1/skillsskills:read
POST/v1/skillsskills:create
GET/v1/skills/{id}skills:read
POST/v1/skills/{id}/scanskills:create
POST/v1/skills/{id}/approveskills:update
POST/v1/skills/{id}/revokeskills:update
GET/v1/skills/{id}/scansskills:read
GET/v1/skills/{id}/scans/{scanId}skills:read
GET/v1/skills/scan-rulesskills:read
POST/v1/skills/scan-rulesskills: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
StatusMeaning
pending_scanIngested, awaiting scan
scanningScan in progress
quarantinedScan found suspicious or malicious content
approvedClean scan or manual approval; skill is signed
revokedPreviously approved; revoked due to advisory

Scan verdicts

VerdictMeaningAuto-action
cleanNo findingsAuto-approved (signature applied)
suspiciousMedium / high findings, no criticalAuto-quarantined
maliciousCritical 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 never echo malicious content

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..."
}
FieldTypeRequiredDescription
namestringYesDisplay name
descriptionstringNoFree-form description
source_urlstringNoOrigin URL (e.g. GitHub raw URL)
source_hubstringNocursor, github, community, or any custom value
contentstringYesFull 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

QueryTypeDescription
statusstringpending_scan, scanning, quarantined, approved, revoked
source_hubstringFilter by source hub
limitintegerDefault 100, max 1000
offsetinteger
{
"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
}
FieldTypeRequiredDescription
rule_typeenumYespattern_match, domain_blocklist, domain_allowlist, executable_blocklist, semantic
namestringYesUnique within rule type
patternsstring[]YesStrings, glob patterns, or regex (per rule_type)
severitystringYeslow, medium, high, critical
enabledbooleanNoDefault true

GET, POST, PATCH, DELETE follow the standard CRUD shape on /v1/skills/scan-rules[/...].


Errors

HTTPerror.codeCause
400validation_errorBody or query failed schema validation
401unauthenticatedMissing or invalid credential
403forbiddenCaller lacks the required skills:* permission
404not_foundSkill, scan, or rule not found
409conflictSkill already in target status (e.g. approve while approved)
429rate_limitedPer-organization rate limit exceeded

Next steps