nocturnusai-knowledge — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited nocturnusai-knowledge (Agent Skill) and scored it 45/100 (orange). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 1 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 1 flagged
A base64 string of 128+ characters appears in a documentation file. Encoded prompt injection hides the hostile instruction in base64 — invisible to keyword filters — and relies on the agent's ability to decode it at runtime. There is no normal authoring reason to embed a multi-hundred-byte base64 blob in skill docs.
*.sig, SIGNATURES) outside the documentation.Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Core CRUD operations for the NocturnusAI knowledge base: asserting facts, teaching rules, querying via inference, retracting knowledge, bulk operations, aggregation, and schema discovery.
Prerequisites: Server must be running and tenant created. See the nocturnusai-connect skill for setup.
Variables use the ? prefix. This convention is enforced everywhere — MCP, REST, CLI, and SDKs.
| Wrong | Right | Why |
|---|---|---|
X | ?x | Bare uppercase is treated as a literal string |
$x | ?x | $ is not recognized as a variable marker |
Who | ?who | No prefix means it is a ground term, not a variable |
? | ?x | Bare ? is not a valid variable name |
The args field is always a JSON array of strings: ["alice", "?x", "42"]. Numbers passed as strings are matched literally. Variables begin with ? and unify with any value during inference.
tell — Assert a FactStore a fact in the knowledge base.
Required params: predicate (string), args (string array)
Optional params: scope, negated (bool), ttl (ms), validUntil (epoch ms), confidence (0.0–1.0), conflictStrategy (REJECT|NEWEST_WINS|CONFIDENCE|KEEP_BOTH)
MCP example:
curl -X POST http://localhost:9300/mcp \
-H "Content-Type: application/json" \
-H "X-Database: default" \
-H "X-Tenant-ID: default" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "tell",
"arguments": {
"predicate": "likes",
"args": ["alice", "bob"],
"confidence": 0.95
}
}
}'REST equivalent:
curl -X POST http://localhost:9300/tell \
-H "Content-Type: application/json" \
-H "X-Database: default" \
-H "X-Tenant-ID: default" \
-d '{
"predicate": "likes",
"args": ["alice", "bob"],
"confidence": 0.95
}'ask — Query via InferenceFind all answers by matching facts and applying rules through multi-step logical reasoning. Use ?-prefixed variables for unknowns.
Required params: predicate (string), args (string array)
Optional params: scope, withProof (bool — include reasoning chain), minConfidence (0.0–1.0)
MCP example:
curl -X POST http://localhost:9300/mcp \
-H "Content-Type: application/json" \
-H "X-Database: default" \
-H "X-Tenant-ID: default" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "ask",
"arguments": {
"predicate": "likes",
"args": ["alice", "?who"],
"withProof": true
}
}
}'REST equivalent:
curl -X POST http://localhost:9300/ask \
-H "Content-Type: application/json" \
-H "X-Database: default" \
-H "X-Tenant-ID: default" \
-d '{
"predicate": "likes",
"args": ["alice", "?who"],
"withProof": true
}'Note: ask returns plain text, not structured JSON. Parse the response body as a string.
teach — Define a RuleTeach a Horn clause rule. When all body conditions are true, the head conclusion becomes derivable.
Required params: head (object with predicate and args), body (array of condition objects)
Optional params: scope
Body atoms can include negated: true (explicit negation, sets truthVal=false) or naf: true (negation-as-failure, succeeds when the atom cannot be proven). See the nocturnusai-reasoning skill for full NAF details.
MCP example:
curl -X POST http://localhost:9300/mcp \
-H "Content-Type: application/json" \
-H "X-Database: default" \
-H "X-Tenant-ID: default" \
-d '{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "teach",
"arguments": {
"head": {"predicate": "grandparent", "args": ["?x", "?z"]},
"body": [
{"predicate": "parent", "args": ["?x", "?y"]},
{"predicate": "parent", "args": ["?y", "?z"]}
]
}
}
}'REST equivalent:
curl -X POST http://localhost:9300/teach \
-H "Content-Type: application/json" \
-H "X-Database: default" \
-H "X-Tenant-ID: default" \
-d '{
"head": {"predicate": "grandparent", "args": ["?x", "?z"]},
"body": [
{"predicate": "parent", "args": ["?x", "?y"]},
{"predicate": "parent", "args": ["?y", "?z"]}
]
}'forget — Retract a FactRemove a specific fact. Any knowledge derived from it is automatically retracted (cascading retraction via truth maintenance).
Required params: predicate (string), args (string array)
Optional params: scope
MCP example:
curl -X POST http://localhost:9300/mcp \
-H "Content-Type: application/json" \
-H "X-Database: default" \
-H "X-Tenant-ID: default" \
-d '{
"jsonrpc": "2.0",
"id": 4,
"method": "tools/call",
"params": {
"name": "forget",
"arguments": {
"predicate": "likes",
"args": ["alice", "bob"]
}
}
}'REST equivalent:
curl -X POST http://localhost:9300/forget \
-H "Content-Type: application/json" \
-H "X-Database: default" \
-H "X-Tenant-ID: default" \
-d '{"predicate": "likes", "args": ["alice", "bob"]}'bulk_assert — Batch InsertAssert multiple facts in a single call. Non-transactional: each fact is attempted independently. Contradictions are reported as errors without aborting the batch. Returns success and failure counts.
Required params: facts (array of fact objects, each with predicate, args, and optional negated, scope, ttl, validUntil)
MCP example:
curl -X POST http://localhost:9300/mcp \
-H "Content-Type: application/json" \
-H "X-Database: default" \
-H "X-Tenant-ID: default" \
-d '{
"jsonrpc": "2.0",
"id": 5,
"method": "tools/call",
"params": {
"name": "bulk_assert",
"arguments": {
"facts": [
{"predicate": "color", "args": ["sky", "blue"]},
{"predicate": "color", "args": ["grass", "green"]},
{"predicate": "color", "args": ["sun", "yellow"]}
]
}
}
}'retract_pattern — Wildcard RetractionRetract all facts matching a pattern. Use ?-prefixed variables as wildcards. Returns the count and list of retracted facts.
Required params: predicate (string), args (string array with ? wildcards)
Optional params: scope
MCP example — retract all color facts regardless of arguments:
curl -X POST http://localhost:9300/mcp \
-H "Content-Type: application/json" \
-H "X-Database: default" \
-H "X-Tenant-ID: default" \
-d '{
"jsonrpc": "2.0",
"id": 6,
"method": "tools/call",
"params": {
"name": "retract_pattern",
"arguments": {
"predicate": "color",
"args": ["?x", "?y"]
}
}
}'predicates — List All PredicatesLists all predicates currently stored, with their argument count (arity) and fact count. Use this to understand the knowledge base structure before querying.
Optional params: scope
MCP example:
curl -X POST http://localhost:9300/mcp \
-H "Content-Type: application/json" \
-H "X-Database: default" \
-H "X-Tenant-ID: default" \
-d '{
"jsonrpc": "2.0",
"id": 7,
"method": "tools/call",
"params": {
"name": "predicates",
"arguments": {}
}
}'Example response (in result.content[0].text):
likes/2: 3 facts
parent/2: 4 facts
color/2: 3 facts
human/1: 2 factsaggregate — Compute Over FactsPerform COUNT, SUM, MIN, MAX, or AVG over facts matching a pattern.
Required params: predicate (string), args (string array), operation (string)
Optional params: argIndex (0-based integer — required for SUM/MIN/MAX/AVG, ignored for COUNT), scope
The argIndex parameter specifies which argument position holds the numeric value to aggregate. For example, in score("alice", "85"), the numeric value 85 is at argIndex=1.
COUNT example — count all score facts (no argIndex needed):
curl -X POST http://localhost:9300/mcp \
-H "Content-Type: application/json" \
-H "X-Database: default" \
-H "X-Tenant-ID: default" \
-d '{
"jsonrpc": "2.0",
"id": 8,
"method": "tools/call",
"params": {
"name": "aggregate",
"arguments": {
"predicate": "score",
"args": ["?player", "?points"],
"operation": "COUNT"
}
}
}'SUM example — sum all scores (argIndex=1 points to the numeric value):
curl -X POST http://localhost:9300/mcp \
-H "Content-Type: application/json" \
-H "X-Database: default" \
-H "X-Tenant-ID: default" \
-d '{
"jsonrpc": "2.0",
"id": 9,
"method": "tools/call",
"params": {
"name": "aggregate",
"arguments": {
"predicate": "score",
"args": ["?player", "?points"],
"operation": "SUM",
"argIndex": 1
}
}
}'Available operations: COUNT, SUM, MIN, MAX, AVG.
1. tell predicate:"parent" args:["alice","bob"]
2. tell predicate:"parent" args:["bob","charlie"]
3. ask predicate:"parent" args:["alice","?child"]
→ returns "bob"1. tell predicate:"parent" args:["alice","bob"]
2. tell predicate:"parent" args:["bob","charlie"]
3. teach head:{"predicate":"grandparent","args":["?x","?z"]}
body:[{"predicate":"parent","args":["?x","?y"]},
{"predicate":"parent","args":["?y","?z"]}]
4. ask predicate:"grandparent" args:["?who","charlie"]
→ returns "alice"1. predicates → see what predicates exist and their fact counts
2. ask predicate:"likes" args:["?x","?y"] → enumerate all likes facts
3. aggregate predicate:"likes" args:["?x","?y"] operation:"COUNT" → count them1. bulk_assert facts:[
{"predicate":"city","args":["paris","france"]},
{"predicate":"city","args":["london","uk"]},
{"predicate":"city","args":["tokyo","japan"]}
]
2. predicates → verify city/2 shows 3 facts
3. ask predicate:"city" args:["?name","france"] → returns "paris"When asserting a fact with the same predicate and args as an existing fact, the conflictStrategy parameter controls behavior.
| Strategy | Behavior | When to Use |
|---|---|---|
REJECT | Error on duplicate (default) | Strict data integrity; prevent accidental overwrites |
NEWEST_WINS | Replace existing fact silently | Mutable state (e.g., current location, status) |
CONFIDENCE | Keep the fact with the highest confidence score | Multiple sources with varying reliability |
KEEP_BOTH | Store both facts side by side | Tracking history or multiple perspectives |
Example — update a mutable property:
{
"predicate": "location",
"args": ["alice", "london"],
"conflictStrategy": "NEWEST_WINS"
}conflictStrategy causes an error. Use NEWEST_WINS for mutable facts or KEEP_BOTH to retain history.argIndex to know which argument position holds the number. COUNT does not need it — it just counts matching facts.X, $x, or Who without ? treats the value as a literal ground term, not a variable. The query will match nothing (or the wrong thing)."default" but REST routes throw ValidationException if the header is missing. Always include it."42" must be passed as strings in the args array. Aggregation operations parse them as numbers internally.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.