indykite-ciq-create-node-with-link — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited indykite-ciq-create-node-with-link (Agent Skill) and scored it 91/100 (green). 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
The text {match} is the classic direct prompt-injection phrasing. Placed in a skill body that the agent reads as trusted instructions, it tries to make the agent abandon its prior rules and follow whatever comes next — a full system-prompt override.
ignore/disregard/forget … previous instructions sentence.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.
Create a brand-new node in the IndyKite Graph (IKG) and wire it to one or more existing nodes in a single atomic POST /contx-iq/v1/execute call. The policy whitelists both a node label and one or more relationship triples, and the Knowledge Query carries both upsert_nodes (for the new node) and upsert_relationships (for the new edge(s)); the new node's variable name from upsert_nodes is referenced as the source or target in upsert_relationships. It combines the patterns from indykite-ciq-create-node and indykite-ciq-create-relationship.
This is the canonical "ingest a new entity into the graph" pattern - used in the IndyKite developer-hub resources for the insurance Contract example (policyAllowWriteContract + knowledgeQueryAllowWriteContract), where one execute creates a new Contract node and wires it via two relationships (COVERS to a Vehicle, ACCEPTED from a Person).
Other paths are deliberately out of scope:
indykite-ciq-create-node.indykite-ciq-create-relationship.Activate this skill when the user:
Contract node linked to an existing Vehicle and an existing Person;Comment node linked to an existing Document;external_id and the source/target endpoints from input_params;403 / 422 from a combined create execute that should have wired the new node up.Do not activate this skill when the user:
indykite-ciq-create-node;indykite-ciq-create-relationship;PROJECT_GID - both used to create the policy and Knowledge Query.$param.Subject type - pick one. The schema is identical across both choices; only subject.type, the filter, and the execute-time auth differ:
| Subject | Use when | Auth at execute time | Filter convention |
|---|---|---|---|
_Application | System-side / ETL / catalog work; no user in the loop. | X-IK-ClientKey only. | subject.external_id = $_appId (reserved). |
Person / User | The authenticated user is performing the operation themselves. | X-IK-ClientKey + Authorization: Bearer <token>. | subject.external_id = $token.sub. |
A policy is restricted to a single subject type - if both should be allowed, write two policies. The runnable example below uses _Application (insurance-contract ingestion); a Person variant - for example, a user posting a new Comment linked to an existing Document they own - differs only in subject.type, the filter, and the execute headers.
Cypher pattern - must MATCH the subject and every existing endpoint the new node will link to. The new node itself is not matched; it's declared in upsert_nodes.
Working example (used throughout this skill, taken verbatim from the developer-hub policyAllowWriteContract resource):
An_Applicationcreates a newContractnode and links it via:COVERSto an existingVehicle(owned by an existingCompany) and via:ACCEPTEDfrom an existingPerson.
MATCH (subject:_Application)-[r1:HAS_AGREEMENT_WITH]->(company:Company)-[r2:OWNS]->(vehicle:Vehicle)
MATCH (person:Person)Variables: subject, r1, company, r2, vehicle, person. The new Contract node will be declared as a fresh name in upsert_nodes; the two new relationships will reference vehicle, person, and the fresh name as endpoints.
node_types and relationship_typesBuild the policy JSON with five blocks:
meta.policy_version - currently 1.0-ciq.subject.type - _Application for the running example.condition.cypher and condition.filter - the cypher matches the subject and existing endpoints; the filter pins them by external_id ($_appId plus $vehicleID, $personID).allowed_upserts.nodes.node_types - the new node's label (e.g. ["Contract"]).allowed_upserts.relationships.relationship_types - one triple per new relationship, matching the directions and labels.A complete combined-create policy for the running example: see assets/policy-create-contract.json.
Create it through the Config API:
# set the current project_id, and stringify only the `policy` field, before POSTing
jq --arg pid "$PROJECT_GID" '.project_id = $pid | .policy |= tojson' indykite-ciq-create-node-with-link/assets/policy-create-contract.json \
| curl -X POST "$API_URL/configs/v1/authorization-policies" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $SERVICE_ACCOUNT_TOKEN" \
-d @-A 201 Created returns the policy's id (GID). Export it as POLICY_ID - the Knowledge Query create injects it into policy_id.
For the schema deep-dive (how node_types and relationship_types interact, why direction matters, what existing_nodes would add) see references/policy-reference.md.
upsert_nodes and upsert_relationshipsThe Knowledge Query has two write arrays:
`upsert_nodes` - declares the new node. Same shape as in indykite-ciq-create-node:
name - fresh variable name (not in cypher), e.g. contract.type - node label, must match allowed_upserts.nodes.node_types.external_id - required for new nodes; usually $param.properties - array of {type, value, metadata?} items.`upsert_relationships` - declares each new relationship. Same shape as in indykite-ciq-create-relationship, with one important twist:
name - fresh variable name for each new relationship (e.g. r3, r4).source - variable name. Can be a cypher variable (existing node) or the `name` of an `upsert_nodes` entry (the just-created node).target - same: cypher variable or upsert_nodes name.type - must match the policy's relationship_types.That source/target flexibility is what makes the combined operation work: r3 connects the just-created contract to the existing vehicle; r4 connects the existing person to the just-created contract.
A complete combined-create Knowledge Query for the running example: see assets/knowledge-query-create-contract.json.
Create it through the Config API:
# set the current project_id and policy_id, and stringify only the `query` field, before POSTing
jq --arg pid "$PROJECT_GID" --arg polid "$POLICY_ID" '.project_id = $pid | .policy_id = $polid | .query |= tojson' indykite-ciq-create-node-with-link/assets/knowledge-query-create-contract.json \
| curl -X POST "$API_URL/configs/v1/knowledge-queries" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $SERVICE_ACCOUNT_TOKEN" \
-d @-A 201 Created returns the Knowledge Query's id (GID).
Schema details (which arrays interact, response shape covering both new nodes and new relationships) live in references/knowledge-query-reference.md.
The execute endpoint is the same as for every other CIQ operation:
POST <API_URL>/contx-iq/v1/executeFor the _Application subject:
X-IK-ClientKey: <AppAgent-credentials-token> - required.Authorization: Bearer … - omit.Request:
{
"id": "<knowledge_query_gid_or_name>",
"input_params": {
"vehicleID": "car2",
"personID": "ryan",
"contract_external_id": "ct853",
"contractNumber": "rbjh853"
}
}A runnable shell helper: scripts/execute.sh.
Full execute reference: references/execution-reference.md.
A successful combined-create returns the new node's projection plus the new relationships' identifiers:
{
"data": [
{
"nodes": {
"contract.external_id": "ct853",
"contract.property.number": "rbjh853"
},
"relationships": {
"r3": { "Id": …, "ElementId": "…", "StartId": …, "EndId": … },
"r4": { "Id": …, "ElementId": "…", "StartId": …, "EndId": … }
}
}
]
}If the response is not what you expected, walk this list:
upsert_nodes[].type must be in allowed_upserts.nodes.node_types, and each upsert_relationships[] triple must be in allowed_upserts.relationships.relationship_types. Either mismatch → 403.vehicle, person) must resolve to a real node. If MATCH finds no rows, the operation has nothing to wire - 200 with empty data.name in upsert_nodes (e.g. contract) must be exactly the same string used in upsert_relationships[].source or target. Typos here silently produce wiring failures.(Contract)-[:COVERS]->(Vehicle) is different from (Vehicle)-[:COVERS]->(Contract).contract_external_id, contractNumber, vehicleID, personID all need to be in input_params.For other failure modes see references/troubleshooting.md.
When this skill has been applied successfully:
subject.type, a Cypher pattern matching the subject and existing endpoint nodes, partial filters, and both allowed_upserts.nodes.node_types and allowed_upserts.relationships.relationship_types populated.upsert_nodes and one or more new relationships in upsert_relationships (with the new node's name referenced as a source or target).POST /contx-iq/v1/execute returns the new node's projection plus the new relationships' identifiers.references/policy-reference.md - combined node_types + relationship_types, optional existing_nodes for hybrid create-and-update flows.references/knowledge-query-reference.md - upsert_nodes + upsert_relationships interaction, source/target cross-referencing, multi-relationship patterns.references/execution-reference.md - request/response, atomicity guarantees, idempotence on rerun.references/troubleshooting.md - 403 / empty-data / wiring-mismatch / cross-reference patterns.assets/policy-create-contract.json - the canonical insurance-Contract example, lifted from policyAllowWriteContract in the developer-hub resources.assets/knowledge-query-create-contract.json - matching Knowledge Query.scripts/execute.sh - Bash helper.This skill uses generic markdown instructions and works across all agents listed in the README. The agent needs to be able to issue HTTP requests. No Claude Code hooks, Cursor @-mentions, or Copilot workspace context are required.
upsert_nodes and upsert_relationships cross-reference.policyAllowWriteContract and knowledgeQueryAllowWriteContract - the canonical insurance-Contract example this skill is built around.kqb write variants for context-aware ingestion patterns.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.