| generate_threat_modelA | Generate a threat model from a feature description: trust
boundaries, assets (Confidentiality, Integrity, Availability, Usage),
capability-defined attackers, the control objective matrix and
assumptions. A multi-step pipeline; progress is reported automatically. Similar models: where an existing model of the workspace overlaps
the description, nothing is generated: {"similar_models": [{"id", "title", "reason"}], "suggestion"}. Refine a candidate with
refine_threat_model (usually right: it keeps controls and history),
or retry with force=True once the operator confirms it is distinct. Saved first: where the platform models what the system does, the
model is saved holding the description and generated from it later.
extracting: true (model_id, job): its behaviour is extracted
in the background. needs_answers: true (model_id,
questions): RELAY THE QUESTIONS TO THE PERSON and answer them with
answer_behavioural_questions. Then poll get_behavioural_model
until generation_ready is true and call
generate_from_behavioural_model with the model_id (next says
so). No control build is proposed until then. Deprecated: model_id
here; with an empty description it does the same, with one it is
refused (resume_takes_no_description). Answer: {model_id, version, title, asset_count, attacker_count, control_objective_count, controls_status, ...}. proposed: a build
is proposed (proposal: what it builds, estimated_credits, the
model_version, set_revision and scope_revision a start
names); review it with the user, then start_control_build with
confirm_estimate=True. none_owed: nothing is owed;
unavailable / error: read get_control_generation_status. A
governor at warning or exhausted: relay it; queued work
resumes at resets_at, never dropped. |
| generate_from_behavioural_modelA | Generate the threat model of a model awaiting its first generation,
from its behavioural model. Mutating; saves the model's next version. A generation where the platform models what the system does saves the
model first (generate_threat_model answers extracting or
needs_answers). Once get_behavioural_model reports
generation_ready: true, call this with its model_id: the
inventory steps run from the stored description and the answers to its
questions. It takes no description. Progress is reported automatically;
it takes minutes. Answer: as generate_threat_model's, controls_status and the
proposed control build included; needs_answers with the
questions when it stopped on questions. Refusals come back as data, {generated: false, http_status: 409, code, message}: not_awaiting_generation (the model has been generated,
or its behavioural model still asks questions or is being extracted:
read get_behavioural_model, and refine a generated model with
refine_threat_model), generation_in_progress (another generation
of the model is running: do not start another; poll
get_behavioural_model, whose generation_ready turns false and
whose version moves when it saves). A model that is not there is a 404
error. |
| refine_threat_modelA | Refine an existing threat model based on an instruction. Updates the model's assets, attackers, trust boundaries, and
control objectives based on the instruction. Creates a new
version. Progress is reported automatically. Refine CANNOT silently replace an entity's identity under a
stable ID or silently drop an entity. Behavior: Preserved entities where the LLM proposed an identity-
bearing rewrite (name / description / security_properties on
assets; capability / archetype / position on attackers) run
through a semantic-preservation guard. Rewrites classified as
replace or ambiguous (or unavailable if the gate
LLM is down) have their identity fields REVERTED to the
pre-refine values. Each rejection shows up as an entry in the
semantic_rejections array in this tool's return value —
surface these to the operator. Entities the LLM drops from the refined output are re-
appended to the model unchanged. The only sanctioned removal
path is remove_entity (entity_type="asset") / remove_entity (entity_type="attacker") (soft-delete). CO IDs are stable across refinements; pairs (asset,
attacker) that disappear come back as tombstones with
removed=True (not renumbered). Controls that only mapped
to tombstoned COs become orphaned at read time.
|
| query_threat_modelA | Ask a natural-language question about an existing threat model. Read-only; no side effects (no new version, no mutation). Uses AI to answer questions grounded in the model's assets, attackers, control objectives, assumptions, and current security posture, returning {model_id, answer} where answer is prose. Use this for interpretation or summary questions ("what are the biggest gaps?", "which attackers target the token store?"). Do NOT use it to change the model — use refine_threat_model for that — and prefer get_threat_model / assess_model when you need structured data (entity lists, coverage counts) rather than a written answer. |
| list_threat_modelsA | List saved threat models in the current workspace. Read-only; no side effects. Returns {items: [{id, title, version, created_at, ...}], count}. Use this to discover model IDs to pass to other tools, or for a portfolio overview. |
| update_threat_modelA | Change a threat model's metadata: its name, its parent, where its
description came from. Mutating; pass only what changes. name (1-120 chars) renames it; no new version. Titles are unique
within a workspace, case-insensitive (409 on a clash).
parent_id wires it under a parent on the recursive composition
tree, so it inherits the parent's topology and objectives;
clear_parent=True makes it a tree root. Cycles (409) and chains
past the platform's maximum depth (400) are refused. No new version.
provenance_kind (code, ticket, document, manual,
mixed) records where the description came from, with the other
provenance_* values. code with provenance_commit_sha means
the code is authoritative and the model follows it
(reconcile_model measures it against the code); any other kind
means the description is intent and the code is measured against it.
Bumps the model version.
Changes apply in that order. Returns {model_id, name?, parent?, provenance?}, one entry per change applied. A failure raises and
names the changes already applied. |
| declare_foundationA | Mark a model as a shared foundation that advertises providable controls. Mutating: records this model as a foundation and stores its advertised controls; other models can then delegate to them (see attach_foundation). A foundation is a shared service (auth, logging, a shared datastore) whose controls other models can rely on. Each entry in provides advertises one of THIS model's controls as providable: {"control_id": "CTRL-07", "capability_label": "Validates session tokens", "description": "..."}. A capability always advertises a control (a proven mechanism), never an objective. |
| list_relianceA | List a model's cross-model dependency edges, in both directions. Read-only; no side effects. Returns {model_id, as_consumer: [...], as_provider: [...]}. Consumer edges are this model's declared delegations / reliances on other models' controls; provider edges are other models relying on this one (its blast radius if its controls change). Use this to inspect existing dependencies before creating or deleting edges (manage_reliance / attach_foundation), or to understand what breaks if this model's controls change. |
| manage_relianceA | Create, confirm or delete one cross-model reliance edge. Mutating. action="create" declares that model_id relies on a provider
control (the target is ALWAYS a control, so credit ends at a proven
mechanism). mode is delegated (this model does not implement the
objective; pass source_objective_id) or relied_upon (this model's
own control depends on the provider's; pass source_control_id). The
provider must be in the same workspace. The edge enters draft, runs
LLM semantic validation, and carries no credit until confirmed. Returns
the edge.
action="confirm" promotes the draft edge_id to active, the
credit-soundness gate: refused unless validation returned valid; a
partial result or a mode mismatch is never silently credited.
Returns the edge.
action="delete" permanently removes edge_id, withdrawing any
credit the consumer derived from it (its coverage can move); neither
model's controls change. Returns {deleted: true, edge_id}.
list_reliance shows a model's edges and their ids.
|
| attach_foundationA | Delegate this model's objectives to a foundation's controls, in bulk. Without selections it is read-only: it returns candidate
(objective ↔ provider control) pairs with a match score, and nothing is
created or credited. Show them to the operator. With selections — a list of {"source_objective_id": ..., "provider_control_id": ...}, typically the confirmed subset of those
candidates — it is mutating: each becomes a delegated draft edge that
runs LLM validation and carries no credit until confirmed with
manage_reliance(action="confirm"). Returns {created, failed}. |
| delete_threat_modelA | Delete a threat model and all associated data. Destructive and permanent — cannot be undone. Mutating: removes the model along with every version, its controls, assertions, findings, attestations, and tag/reliance memberships. Reliance edges from other models that pointed at this one are invalidated, which can move those consumers' posture. Confirm intent before calling. To keep a copy first, use export_report (scope="model", format="archive") (a self-contained, re-importable JSON archive). Returns {deleted: True, model_id}. |
| get_threat_modelA | Get a threat model by ID: its trust boundaries, assets, attackers,
assumptions and components. Read-only. The answers to its questions are counted (clarification_count) and
left out unless include_clarifications=True, which pages them
(clarifications_offset, clarifications_limit: default 50, at most
200; clarifications_next_offset null at the end). A read over 200,000
characters is cut, and cuts says what and how to read it: the
objectives, then the clarifications, then each entity collection,
largest first, kept as rows that name its entities. include_inventory_completeness=True (latest version only) adds
inventory_completeness: how much of the inventory the claims rest on,
with bounded_by (the entities holding the tier down) and
grounding_gaps, keyed by entity id. Each gap names a ground the
entity lacks (position, privileges, extent, components,
flagged, inherited), a detail, and acts: each act's
mcp_tool and mcp_arguments (that tool's arguments, the entity
named and lists comma-separated; fields is the REST body), a value
in angle brackets being yours to fill. Taking every listed act grounds the entity; a gap with no
act says no act can supply it on this deployment. An attestation an act
names is a judgment (see edit_attacker). null when the record
could not be read.
Important for agents reading model state: Assets and attackers may carry deleted: true (soft-deleted).
Exclude these when showing "what's in the model now"; include
them only when discussing history or offering restore. Restore
an entity via restore_entity (entity_type="asset") / restore_entity (entity_type="attacker"). Control objectives may carry removed: true (tombstone — the
(asset, attacker) pair was removed in a later version). Exclude
these from coverage math and LLM prompts; they exist to keep
CO IDs stable so controls referencing them can be detected as
"orphaned" rather than silently rebinding.
|
| import_threat_model_archiveA | Import a JSON audit archive (from export_report (scope="model", format="archive"))
into a target workspace. Mutating: creates a NEW threat model in the target workspace. Requires
write access to that workspace. A fresh model_id is assigned on every
import, so the same envelope can be imported any number of times
without collisions; title collisions in the target workspace
auto-suffix (imported YYYY-MM-DD). Non-destructive — never
overwrites or touches an existing model. Use to move or clone a model between workspaces or across instances;
the envelope round-trips through export_report (scope="model", format="archive")
first. The archive carries the model's current state, and the import creates it
as version 1 of a new model: its controls, live assertions, decisions in
force and open findings. Earlier versions, activity and chat are not
carried. The restored model arrives UNVERIFIED. The tier verdicts on its
assertions, the attested flag on a verification result, and the facts a
verification run reported are the origin's record of what it claimed —
kept with the model as that record, and not credited here: a verdict
belongs to the run that produced it and the judge that decided it, and
this workspace has neither. Verification is earned here by running it
against code this workspace can reach, so plan for a restored model to
read unverified until it has. The same holds for the judgements of its
mitigation groups: the import queues none, and its objectives read
awaiting judgement until someone asks for them. |
| get_control_generation_statusA | A model's proposed control build and its last started one. Read-only. A build runs only when started. proposal (or null): mode,
objective_count, estimated_credits (estimate_basis
history|bootstrap), the model_version / set_revision /
scope_revision start_control_build names, and what to review:
flagged_entities, pending_attestations (attestations that would
remove objectives from the build); flags_evaluated /
attestations_evaluated false when not computed. Poll until
terminal; hint names the next act. status: queued | generating | deferred | pausing | paused | blocked | complete | failed | skipped | discarded | none. deferred
waits for the budget reset; paused keeps its work until
resume_control_generation or discard_control_build. blocked
has code (dependency_unavailable or analysis_incomplete),
message, retry_after_seconds: relay it and retry with
resume_control_generation, never regenerate_controls (re-bills).
Running: ready_cos / target_cos are progress, not coverage.
selfheal_activity is a SAMPLE of a strengthening round: read
refining_total / authoring_total / set_aside_total;
set_aside waits for a person, NOT a failure. complete: figures come from diagnosis: covered,
uncovered, undecided (strengthen_controls), judging
(wait), not_judged (judge_objectives), no_group (group),
grouping, no_control (start the build), awaiting_attestation
(attest), awaiting_assumption (review queue), mitigated_elsewhere
(nothing owed). covered_cos of judged_cos;
awaiting_judgement_cos = not_judged + judging;
excluded_cos, dispositioned_cos the rest. analysis_pending:
figures may still move.
|
| pause_control_generationA | Pause a model's background control generation. Mutating. Use when the user asks to stop a build — for example one started by
mistake — or before deleting a model whose controls are still being
built. A running build stops at its next step (status pausing,
then paused); a queued or waiting one is paused at once. Everything
already done is kept in the build's staging copy; nothing is published,
nothing new is started or billed, and nothing resumes it except
resume_control_generation. A paused build still holds the model: to
drop it instead, call discard_control_build once it shows paused.
Pausing is idempotent. Returns one of: {paused: true, model_id, status, status_detail} — status is
pausing (still stopping) or paused.
{paused: false, http_status: 409, code: "not_running", status} —
there is no generation to pause; read status.
|
| resume_control_generationA | Resume control generation that was paused, or retry one that stopped
before finishing. Mutating. Use when get_control_generation_status returns status: "paused"
(someone paused it) or status: "blocked" (blocked.code
dependency_unavailable or analysis_incomplete). A paused run
resumes at once. For a blocked one the platform checks the services it
depends on first, so a retry while one is still down costs nothing and
changes nothing. Returns one of: {resumed: true, status: "queued", status_detail} — the run resumes
where it stopped (only the unfinished work, billed to the original
generation). Poll get_control_generation_status until complete.
{resumed: false, http_status: 409, code: "pause_in_progress"} — the
run is still stopping after a pause; resume once it shows paused.
{resumed: false, http_status: 503, code: "dependency_unavailable", message, retry_after_seconds, ...} — still unavailable; relay the
message and try again after retry_after_seconds.
{resumed: false, http_status: 409, code: "retry_too_soon", retry_after_seconds, ...} — a retry was just tried; wait.
{resumed: false, http_status: 409, code: "not_blocked", status} —
nothing is paused; read status.
|
| strengthen_controlsA | Strengthen a model's controls: work on the objectives whose mitigation
groups the background judge found do not cover them (the uncovered
and undecided of get_control_generation_status's diagnosis).
Mutating only with confirm_estimate=True; consumes credits then.
not_judged objectives have nothing to strengthen from: judge them
first with judge_objectives. Call with confirm_estimate=False (the default): nothing starts or
is charged; the answer carries diagnosis, scope, estimate
(credits, per_objective, basis) and the
model_version / set_revision the model stands at. Show the
user the estimate. Once they agree, call with confirm_estimate=True and those two
values (their review of the model as it stood). A background run
starts (started: true); poll get_control_generation_status.
It holds the model like any build: pausable, resumable, discardable.
A gap only the environment can close (hosting, a third party) is never
answered with a control: an accepted assumption stating it is bound
into the group; otherwise an assumption proposal waits in the
review queue (get_review_queue / decide_proposal) and the objective
counts as awaiting_assumption. A rejected one is not proposed again. Refusals come back as data, {started: false, http_status, code}:
409 review_stale (the model or its controls changed since the
estimate: confirm again with the values returned), 409
generation_active (a build holds the model), 402 (the balance cannot
cover the estimate). |
| judge_objectivesA | Have judged every objective whose mitigation group has no judgement for
its current controls and none queued (the diagnosis's not_judged, or
objectives reading awaiting_judgement). Mutating only with
confirm_estimate=True; may consume credits then. Adding or
implementing controls does not move such an objective; a judgement
does. judging objectives are already queued: wait for them. Call with confirm_estimate=False (the default): nothing is queued
or charged; the answer carries diagnosis, scope, ungrouped
and estimate (credits, objectives, computed_at,
rate_version). Show the user the estimate. Once they agree, call with confirm_estimate=True: each objective
in scope is queued (confirmed: true, queued), metered at
actuals as it runs, and status_detail is the fresh status.
ungrouped objectives have no mitigation group and are never judged:
group their controls with set_mitigation_groups first.
A judgement is not a repair: it can come back insufficient or undecided,
which counts the objective as uncovered or undecided, work for
strengthen_controls. judge_objective does the same for one. Refusals come back as data, {confirmed: false, queued: 0, http_status, code, message}: 409 control_generation_in_progress
(poll get_control_generation_status), 402 insufficient_credits /
quota_exceeded (with estimated_credits), 503 (judging
unavailable). An unknown id in co_ids is a 400 error. |
| regenerate_controlsA | Propose a regeneration of the model's controls. Starts nothing. A regeneration re-authors controls from the current COs; its publish
creates the next model version. Controls whose descriptions survive
unchanged KEEP their implementation status, evidence, notes, assertions,
and Jira / compliance mappings. Controls whose descriptions change or
disappear are soft-deleted (still queryable via
get_controls(include_deleted=True)). When co_ids is given, only
those COs' controls are regenerated — all other controls are left as-is. This tool records the regeneration as the model's PROPOSED build and
returns at once with status: "proposed" and proposal (mode,
objective_ids, objective_count, estimated_credits, and the
model_version, set_revision and scope_revision a start must
name). A proposal
merges with any already proposed for the model, the broader one winning.
Show the user what it would build and cost; once they agree, call
start_control_build with those values and confirm_estimate=True.
A build someone started holds the model, so this is refused (409
generation_active) until it finishes, or is resumed and finishes, or
is discarded. To rebuild everything, omit co_ids. To fix only stale/orphaned CO
mappings without re-authoring control text, prefer remap_control
(mechanical, no LLM). |
| start_control_buildA | Start the model's proposed control build. Mutating only with
confirm_estimate=True; consumes credits then. A model's controls are built only by a build someone starts. Generating
or refining the model, editing an entity and regenerate_controls
each PROPOSE one; get_control_generation_status shows it as
proposal. Call with confirm_estimate=False (the default). Nothing starts and
nothing is charged; the answer is {started: false, proposal, message}, the proposal carrying a fresh estimated_credits and
the model_version, set_revision and scope_revision the
model stands at. Show the user what it would build and cost, and
what it lists to review. Call again with confirm_estimate=True and those three values once
they have reviewed the model. The build starts ({started: true, job_id, model_version, status: "queued", status_detail}) and holds
the model until it publishes, fails or is discarded: other writers of
the model's controls are refused meanwhile, and reads show the last
published controls. Poll get_control_generation_status until
terminal; the publish is one step, after which get_controls
shows the result.
Refusals come back as data: {started: false, http_status: 404, code: "no_proposal"} — nothing
is proposed for the model.
{started: false, http_status: 409, code: "review_stale", proposal, model_version, set_revision, scope_revision, estimated_credits} —
the model, its controls or the objectives the build would author
changed since the values were read (or none were named). Review again
and start with the values returned.
{started: false, http_status: 409, code: "generation_active", status} — a build already holds the model.
{started: false, http_status: 402, ...} — the balance this
workspace bills to cannot cover the estimate.
|
| discard_control_buildA | Discard a model's held control build. Mutating. A build that is queued, deferred, paused or blocked holds
the model without running. Discarding it drops everything it staged — the
model keeps its published controls exactly as they were — releases the
model, and proposes the build again so it can be started later. The
credits it already consumed are not returned. A running build must be
paused first (pause_control_generation, then wait for paused). Returns one of: {discarded: true, model_id, status: "discarded", proposal, status_detail}.
{discarded: false, http_status: 409, code: "pause_first", status} —
the build is running; pause it first.
{discarded: false, http_status: 409, code: "not_held", status} —
no build holds the model.
|
| list_control_revisionsA | List every change to a model version's set of controls. Read-only. Each write to a version's published controls — a build's publish, an
import, an edit, a deletion, an undo — is a set revision with its author.
Returns {model_id, model_version, latest_version, discarded, revisions, undo_target}; each revision carries revision, job_id (the
build that wrote it, if any), started_by, started_at,
controls (the ids it touched), undo_of (the revision it undid,
for an undo) and undone_by / undone_at. undo_target is the
revision undo_model_change(target="controls") would undo (null when none, and for any
version but the latest). discarded is true for a version a revert
replaced. |
| undo_model_changeA | Undo the latest change to a model's controls, or revert its latest
version. Mutating. target="controls" restores exactly what the latest set revision of
the latest version replaced (list_control_revisions'
undo_target). Changes are undone latest first, one per call; the
undo is itself recorded, and the next undo goes to the change before
it. There is no redo. Verdicts the undo returns to are served again
rather than re-judged. Answers {applied: true, model_id, model_version, undone, revision, controls}: undone is the revision
undone, revision the one the undo recorded, controls the ids it
restored.
target="version" creates a new version copying the latest earlier
version not already discarded — the model, its controls and their
objective metadata — and marks the replaced version discarded. Version
numbers are never reused, and the discarded version stays readable in
the history. Findings on controls the revert removes are resolved.
Answers {applied: true, model_id, model_version, copied_from, discarded}: model_version is the new version.
A refusal comes back as {applied: false, http_status: 409, code, message}: generation_active (a build holds the model), and for
controls nothing_to_undo or set_diverged (a control the change
touched has changed since; undo the later change first), for a version
no_earlier_version. |
| update_control_statusA | Update the implementation status of a security control. Mutating. Sets the control's status to "implemented" or "not_implemented".
Marking a control "implemented" REQUIRES at least one assertion on the
control — check its assertion_count (via get_controls) first and
submit assertions with submit_assertions if it is zero, or the call
is rejected. |
| refine_controlA | Refine a control's description with AI-gated CO sufficiency check. Two modes: Provide description: proposes a new description directly. Provide codebase_findings: the platform proposes a description
based on existing code that may already satisfy the control. Both can be provided: the platform evaluates the proposed
description with the codebase findings as context.
The AI evaluates whether the mitigation group still collectively
satisfies all mapped control objectives. If rejected, returns
{accepted: false, reason, per_co} with per-CO reasoning. A refinement is rejected when the proposed description would reduce the
protection the control currently states for an objective it is mapped to;
per_co names each objective and explains why. This is a decision, not a
transient error — re-wording the same narrowing will not pass it, and it
applies however well-motivated the narrowing is. A control is a requirement
that must be met to cover its objectives, so evidence that the system does
not currently meet it means the control is UNMET, never that the control
should ask for less. After an accepted refinement the control's assertions are kept
and judged again against the new description in the background: an
assertion that still fits keeps counting as evidence, and one that no
longer fits is flagged as not aligned with the control. Read
get_sufficiency once that re-judgement lands, and replace the
assertions it names. The refinement itself supersedes nothing; the
response's superseded_assertions is always 0. |
| remap_controlA | Mechanical, non-AI-gated remap of a control's CO mappings. Distinct from refine_control (AI-gated description edit) and
set_mitigation_groups (AI-gated CO-centric group authoring).
Use remap_control when the operator already knows the correct
co_ids and just needs to persist the mapping change — e.g.,
restoring mappings after an asset/attacker edit left the control
with stale or orphaned CO references. No LLM evaluation runs. Rejects target co_ids that do not exist on the model or are
tombstoned (the pair was removed in a later version) — map to
live COs only. |
| apply_control_changesetA | Apply a batch of control operations atomically as ONE transaction. Use this to reorganize a model's controls in a single step — for example
to deduplicate controls (remap several onto the right objectives and delete
the redundant ones at once), instead of many separate calls. All operations
commit together or not at all. Mapping-only: remap/delete/set_groups change objective mappings and retire
controls but never re-author a control's description, so a kept or reused
control keeps its status, evidence, and assertions. The orphan guard is
evaluated on the FINAL state of the batch, so a delete paired with a
covering remap or add in the same changeset is allowed; a changeset that
would leave any previously-covered control objective uncovered is rejected
as a whole and nothing is written. |
| model_coherence_reportA | How coherent a model's structure is: its component bindings, the
repos its controls' assertions name, and whether every CO is
structurally reachable. Read-only. Pass co_id to keep only the findings about that CO (404 if it does
not exist). Each finding carries type, severity, a message
and the ids it concerns, so its fix can be called directly: control_component_unknown, assertion_repo_orphan,
control_unscoped_with_scoped_assertions:
assign_to_components(target_type="control").
assertion_repo_mismatch: rebind the assertion or rescope the
control.
asset_component_unknown: edit_asset with corrected
component_ids.
component_unbound: for your own code, edit_component with the
real repo_url. An external-zone component (a third-party service,
a customer's IdP) stays unbound: the finding is a permanent marker of
an external dependency, not a TODO, and client code touching it is no
reason to bind it.
co_attacker_unpositioned: edit_attacker with
trust_boundary_ids. co_asset_unbounded:
assign_to_components(target_type="asset").
co_no_shared_boundary: reposition the attacker or rescope the
asset; if the boundaries truly do not meet, that is the answer.
co_missing_entity: restore_entity, or remove the CO.
An indeterminate reachability verdict means the structure it needs is
missing: supply it. It never means the objective is inapplicable, which
is a separate claim recorded with create_co_disposition.
get_reachability_verdicts returns the raw verdicts. |
| get_compositionA | A model's composed view: its own entities with everything inherited
from its ancestors on the recursive tree. Read-only. view selects what is returned:
overview (default, ~1-2KB, read it first): {model_id, model_version, flag_enabled, tree: {parent_id, ancestor_chain, depth, child_ids}, counts: {entities, control_objectives, reconciliation_candidates}, warnings}.
entities: the effective entity set keyed by kind (trust
boundaries, components, assets, attackers, attack paths), each entry
{kind, qualified_id, owner_model_id, owner_title, origin, entity}.
Paginated (page, page_size); kind (e.g. "attackers")
keeps one kind.
objectives: effective COs {co_qid, asset_qid, attacker_qid, security_properties, origin}, where origin is own,
cross (inherited, with a local asset or attacker) or
inherited.
coverage: per effective CO {co_qid, is_covered, own_credit, inherited_credit, contributing_controls} — the composed figures, not
the per-model get_verification_report. Paginated; origin
filters by contributing-control origin.
attack_paths: {effective_paths, lattice_positions, authored_paths, suggestions: {missing_path, dangling_path}} against
the composed topology.
Where composition is not available, every view returns its shape empty
with flag_enabled: false rather than an error. Composed reachability
is get_reachability_verdicts(composed=True). |
| decide_reconciliation_candidateA | Decide a reconciliation candidate from
list_reconciliation_candidates. Mutating. kind is assets, attackers or components; own_qid /
inherited_qid are the pair's qualified ids ("child:A1",
"parent:A1").
decision="apply" records that the descendant's own entity IS the
inherited one: the own entity stays in the model and is left out of its
composed view, so the inherited entity is canonical and credit keys on
it. The record is dropped when the pair stops matching (an edit, a
re-parent). A heuristic-tier candidate is
refused unless confirm_heuristic=True acknowledges its structural
divergence. The server re-validates the pair (400 when the model has
moved since: refresh the list). Bumps the model version; returns
{model, controls_carried, controls_orphaned, orphaned_control_ids}, model being its id, version and title.
decision="reject" records "these are NOT duplicates" at org scope,
so the pair leaves the active queue for everyone. Idempotent on the
pair; no new version. Returns the record; keep its id.
decision="unreject" removes the rejection rejection_id (from
list_reconciliation_candidates(disposition="rejected")), returning
the pair to the queue. Returns {ok: true}.
503 where composition is not available on the backend. |
| lift_composition_entityA | Promote a shared-anchor entity from two sibling descendants to their
lowest common ancestor. Mutates state across THREE models. The operator has confirmed (via the composition lift-candidate view)
that the entity local_id_a on descendant_a_id and the entity
local_id_b on descendant_b_id are the same logical thing and
should be modeled once on the LCA. The route's model_id is the
operator's current context model — typically the LCA, but the server
accepts any ancestor of both descendants. Conflict resolution. The server re-detects field-level and
attached-state conflicts against current live state before applying.
If new conflicts have surfaced since the operator's last candidate
fetch, the call returns 400 with the missing conflict keys; refresh
the lift-candidate view and resubmit with resolutions covering every
key. Each entry in field_resolutions / attached_state_resolutions
is "keep_a" | "keep_b" | "keep_both" (union for list/set
fields; falls back to B for scalars). Over-application gate. The lift extends visibility to every descendant
of the LCA, not just the two source descendants. The server runs an
over-application gate that refuses lifts touching descendants outside
an acknowledged set; pass acknowledged_third_party_subtrees to
acknowledge specific subtrees, or skip_overapplication_gate=True to
override entirely after explicit operator confirmation. Each affected model (LCA + both descendants) bumps version and emits a
model_refined activity event; a structured lift_applied event
with the full lift_event payload lands on the LCA. The audit pack
surfaces this under lift_history. Reverse it with
undo_composition_event(event_type="lift"), which previews unless
dry_run=False; the inverse operation is split_composition_entity. |
| split_composition_entityA | Push an ancestor-owned entity down to one or more descendants and
soft-delete the ancestor's copy. Mutates state across the ancestor +
every target descendant. Inverse of lift_composition_entity. Use when an entity that
currently lives on an ancestor is in fact descendant-specific and
should be modeled separately per descendant — the operator chooses
which descendants take a copy. A new local id is minted on each
target; attached state on the ancestor's entity (assertions, jira
mappings, risk acceptances, etc.) is duplicated to every target. The route's model_id IS the ancestor (the entity being split lives
on it). Each affected model (ancestor + every target descendant) bumps
version and emits a model_refined activity event; a structured
split_applied event with the full split_event payload lands on
the ancestor. The audit pack surfaces this under split_history. |
| get_mitigation_groupsA | Get the current mitigation group structure for a control objective. Returns the grouped view of controls for this CO with details
(id, description, status) for each control: groups: numbered groups (within=AND, across=OR) defense_in_depth: tracked but not required for mitigation unmapped: model controls not mapped to this CO (available for assignment)
Use cases: Before set_mitigation_groups to see the current structure When reviewing a CO's assessment to understand why it is at_risk or mitigated When deciding which unmapped controls to assign to a CO
|
| set_mitigation_groupsA | Declaratively set the mitigation-group structure for a control objective. Mutating; runs as a polled background job (an LLM sufficiency check evaluates whether the new structure satisfies the CO) and returns once complete. Replaces ALL mitigation-group assignments for this CO. Call
get_mitigation_groups first to see the current structure and the
unmapped controls available for assignment. Mitigation groups define alternative paths to satisfy a CO: Within a group: AND — all controls must be implemented. Across groups: OR — any one complete group mitigates the CO. Defense-in-depth: tracked but not required for mitigation.
|
| edit_evidenceA | Attach or detach an auxiliary evidence item (doc, link, artifact
reference) on a control. Mutating. Evidence is contextual metadata only: it does NOT count toward a
control's implementation status, and removing it changes neither the
status nor any assertion. Only assertions prove controls. |
| import_controlsA | Import existing security controls into a threat model. Accepts structured JSON or free-text. Controls are auto-mapped to COs and
deduplicated against existing ones. The parse/map/dedup runs as a background
job (polled for progress), then — because this mutates the model — you are
asked to confirm before the controls are saved. The saved controls are added to the model's current controls as one
change (undoable with undo_model_change); no model version is
created, and it is refused while a control build holds the model. Nothing
runs for them unprompted: the result's awaiting_judgement lists them,
and the mitigation groups they join credit nothing and read awaiting
judgement until judge_imported_controls is called (estimate first,
then confirm_estimate=True). |
| judge_imported_controlsA | Have the imported controls awaiting their judgement judged. Mutating
only with confirm_estimate=True; may consume credits then. Controls saved by import_controls are not judged unprompted: the
mitigation groups they join credit nothing until this runs. It judges
every objective those controls join, priced and charged as
judge_objectives prices and charges them. Call with confirm_estimate=False (the default). Nothing is queued
and nothing is charged; the answer carries awaiting_judgement (the
control ids), co_ids (the objectives they join), scope,
ungrouped and estimate. Show the user the estimate. Call again with confirm_estimate=True once they agree. The
judgements are queued (confirmed: true, queued) and the
controls stop awaiting (awaiting_judgement comes back empty).
ungrouped lists objectives with no mitigation group: nothing can be
judged there until their controls are grouped with
set_mitigation_groups. When nothing awaits, the answer says so and
does nothing.
Refusals come back as data, {confirmed: false, queued: 0, http_status, ...}: 409 while a control build is running, 402 when the
balance this workspace bills to cannot cover the estimate, 503 when
judging is unavailable on this deployment. |
| delete_controlA | Soft-delete a security control, optionally with a justification. Destructive (mutating): the control is retired, not permanently erased. Blocks with HTTP 409 when the control is the ONLY control covering
any control objective — removing it would leave that CO uncovered.
Add a replacement control (or refine the threat model) before
deleting. |
| check_control_gapsA | Analyze control coverage and surface control objectives that lack sufficient controls. Read-only (does not mutate the model); runs as a polled background job and uses LLM reasoning. Complements the deterministic assess_model (which scores each
CO's mitigated / at_risk / unassessed status from control
implementation state) by reasoning about which COs are under-covered
and where new controls are needed. Use this to decide what controls
to add; use assess_model to score the current state. |
| assess_modelA | Run the deterministic assurance assessment over a threat model. Read-only — no LLM calls, no mutation. Evaluates each control objective from its controls' implementation
status and returns summary counts (mitigated / at_risk /
unassessed) plus progressive metrics (defined / implemented /
verified). For LLM-based reasoning about which COs are
under-covered and what controls to add, use check_control_gaps
instead. Use summary_only=True to get just the counts without per-CO
assessments. Paging: a read with limit that leaves pages beyond its own names a
snapshot of the whole assessment, with snapshot_expires_at. Pass
it back with each later page: the pages are then served from that one
evaluation, as of its evaluated_at, so they agree with each other and
nothing is evaluated again. A snapshot is kept for 10 minutes, and only
the 5 newest of a model, so other paged reads of the model can replace
it sooner. One not found comes back as {http_status: 410, code: "snapshot_not_found", message}: read again without it, from the first
page. |
| get_review_queueA | Returns the workspace's review queue: what needs a decision or a
re-check, ranked. Read-only; no side effects. Each row carries an item_type, one of escalation (a judgment an
agent was refused and parked for a person), proposal (an open change
of scope or design, or an assumption proposal: a precondition a
strengthening run found only the environment can meet),
unaccepted_assumption (an assumption something depends on that is
not accepted — never attested, lapsed, or its text changed since it was
attested — with the controls and objectives that wait on it),
open_assumption, behavioural_question (an open question about
what the system does, answered with answer_behavioural_questions),
behavioural_link (an element of the behavioural model not yet linked
to an asset or trust boundary, or linked to one that lacks the property
at stake; see get_behavioural_links), not_applicable_refuted (a
person's statement that a property does not apply to an asset, which
the behavioural model contradicts: under a confirmed model it no longer
counts, under an unconfirmed one it still does; ask the person to revise
or revoke it, or to confirm the model), position_disagreement (an
attested attacker position a producer would place elsewhere),
behavioural_attacker (get_behavioural_attackers),
inventory_judgement (get_inventory_judgements), or
stale_control (an implemented/verified
control whose assertions have not been checked in 90+ days). Rows are
ranked: escalations, then proposals and the behavioural rows by risk
tier, then position disagreements, unaccepted assumptions, open
assumptions and stale controls. Escalations
and proposals are decided with decide_proposal; an unaccepted
assumption is accepted with submit_attestation; for each stale
control, verify its assertions against the codebase. Accepting an
assumption is a person's judgment unless the workspace delegates it.
Start here for periodic maintenance. |
| add_assetA | Add a new asset to a threat model. Creates a new version. Authoring contract: name the data or resource being protected and
the security property at stake (Confidentiality / Integrity /
Availability / Usage), not a mechanism or control ("per-organization
key-wrapping material", not "KMS encryption"). An asset phrased as a
mechanism is flagged with a quality_warning and yields
under-specified control objectives. An asset that does not apply is
recorded with a non-applicability assumption or
create_co_disposition; there is no status to set. The platform reasons the factor decomposition and composes impact
with the prompt generation uses, so factors are calibrated alike;
override one afterwards with edit_asset and a change_reason.
component_ids links the asset to the deployable units that hold it,
which feeds reachability (several for a multi-instance asset, e.g. a
session token on client and cache). A proposal matching a soft-deleted asset is gated: it either restores
that asset (auto_restored: true, restored_asset_id,
discarded_fields; its CO tombstones revive) or is refused as similar
({accepted: false, classification: "similar", candidate_restore_id},
nothing saved). A normal create returns {model, entities, controls_carried, ...}: model is the model's id, version and title,
entities.assets the asset as created. 503: an evaluator is
unavailable, retry with backoff; 502: it answered malformed, retry. |
| edit_assetA | Edit an existing asset. Only provided fields changed. When changing identity fields, hold to the asset authoring
contract: name the data/resource protected and its security
property, not a mechanism — otherwise the result is flagged with a
quality_warning (see add_asset). There is no status field to
set. The composed impact is server-derived from the factor fields;
there is no way to set it directly. To change the rating, set
factor values (the platform composes the new rating) and supply
change_reason documenting the operator override of the
LLM-generated factors. The reason is captured in the
rating-revision audit trail. Over a rating held pending
(rating_pending on get_entity), a factor edit settles the pair
with the rating it sets, a judgment (rating_reconfirmed) a program
makes only under a delegation rule, else parked whole for a person. New words (name, description, security properties) are rated for: one
rating call, at your cost; a rating a person chose is kept and the new
one held pending beside it. Adopting a quality check's advice is
your own edit of the words, stated with its change_reason. LLM-gated on identity-bearing fields (name, description,
security_properties). Factor and notes edits skip the gate. Outcomes when identity fields change: Accepted edit (LLM classifies as preserve) — {model, entities, controls_carried, ...}: the model's id, version and title,
and the asset as it now stands. Rejected edit (LLM classifies as replace /
ambiguous) — {"accepted": False, ...}; nothing saved.
Soft-delete + add-new instead.
Editing a soft-deleted asset is rejected — restore_entity (entity_type="asset")
first. 503 on evaluator outage or when the rating call for new words
fails (nothing saved), 502 on malformed response, 400 when factor
fields are sent without change_reason, 409 when the model changed
since it was read (read it again and send the edit again). |
| add_attackerA | Add a new attacker to a threat model. Creates a new version. Authoring contract: capability names the operations the
attacker can perform from its position and what they achieve —
not just the access or vantage point. Phrase it as "From
[position], the attacker can [concrete operations] …" (e.g. "From
the network path between the API server and the database, the
attacker can read and alter requests and responses to exfiltrate
data in transit or inject forged responses"). A capability that
states only access is flagged with a quality_warning and the
control objectives derived from it may be under-specified. The caller supplies identity-bearing fields (capability, position,
archetype, trust_boundary_ids); the backend reasons the factors.
Override one later via edit_attacker with a change_reason. Three outcomes (normal create / auto-restore / similar-rejection)
mirror add_asset (entities holds the attacker). 503 on
factor-reasoning or restore-candidate evaluator outage, 502 on malformed
restore-candidate response. surface_extent says how much of the reached interface this
attacker's operations range over. An attacker ranging over the whole
interface makes the objectives it appears in for-all obligations,
which only a sound witness (typed_boundary /
sink_default_deny) can credit. Declaring it here is a statement about
the attacker's reach, taken with its change_reason: a person's is
recorded attested; a program's only under a workspace rule delegating
extent_attested, otherwise the attacker is added, the extent lands
unattested and the attestation waits for a person (the answer's
attestation.escalation_id). Only whole
is declarable on a create: narrowing to one named entry is a statement
about the objectives the attacker anchors, and a create has none yet —
add the attacker, then narrow it with edit_attacker and a
change_reason, where the narrowing is checked against the assets
those objectives defend. There is no attacker status to set.
|
| edit_attackerA | Edit an attacker; only the fields given change. capability names the operations performable from the position
("From [position], the attacker can …"), else it gets a
quality_warning. Identity fields are LLM-gated. The composed
likelihood comes from the factors. An edit of attack_vector or
privileges_required creates a model version (reachability reads
both); other factor edits are recorded as rating revisions. Over a
pending rating (rating_pending), a factor edit settles the pair, a
judgment (rating_reconfirmed). New words are rated for (one call, at
your cost); a person's rating is kept, the new one held pending, and
attested or holdings-given privileges never move. Adopting advice is
your own edit. The answer's model is the model's id, version and
title; entities.attackers the attacker as it now stands.
Attesting is a judgment: a person's edit attests what it writes
(trust_boundary_ids, privileges_required, surface_extent)
and attest_* attests the value as it stands. A program attests
only under a workspace rule for that attestation; otherwise its edit
lands unattested and the answer's attestation names the parked
escalation_id (never a 403). A refused program's extent never
lowers an attested one. "point" is REFUSED where an asset of the
attacker's objectives sits on several components and is not
split-knowledge, and never makes a universal clause existential. 400 when factor fields, surface_extent, attest_surface_extent
or attest_privileges come without change_reason; 503/502 on an
evaluator outage or malformed answer or a failed rating call. 409 when
the model changed since it was read: read it again. 409 privileges_from_holdings
for a privileges change on an attacker whose credential classes are
stated: its privileges follow from them, so say what it holds instead
(answer_attacker_credentials). |
| reevaluate_threat_model_factorsA | Re-run the LLM factor judgment on a model's live assets and
attackers: every one, those entity_ids names, only those nobody
rated (unrated_only), or only those whose rating was made for
another description of the system (stale_only, an entity's
rating_stale; with both flags, either). With entity_ids a flag
narrows to those named. The review queue's rating_stale row names the
request that re-rates its entities. Re-baselines factors after a bug fix
or description change, or rates what reads "not rated", without
regenerating the model (which would destroy controls, assertions,
components). One call per entity in scope, at your cost; an id naming no
live asset or attacker is refused (400) before any call; a scope naming
none makes no call. A rating a model made is replaced with a fresh decomposition and its
impact / likelihood re-derived from it. A rating a person chose is never
replaced: the answer is held pending beside it (rating_pending on
get_entity), the more severe of the two in force, until someone
settles them with settle_entity_rating. Each re-rating is a rating
revision recorded with change_reason (default "LLM factor
re-evaluation"), told apart from overrides made with edit_asset /
edit_attacker. The judgment is a starting point: layer
deployment-specific factors on top with those, and a change_reason
saying why. Per-entity soft-fail: a failure on one entity lands in
failed_entities (id, kind, reason) and the rest still
persist; 503 only when every entity in scope failed, with nothing
persisted. Soft-deleted entities are skipped. Re-ratings keep the model version, except where an answer changes an
attacker's attack_vector or privileges_required: reachability
reads both, so the run saves one new version carrying every answer,
and a proposal reviewed before it reads review_stale. Privileges a
person attested, or that what the attacker holds gives, are no rating's:
an answer never moves them. |
| get_verdict_divergenceA | Where the LLM's verdicts disagree with the model's authored state. Two coverage divergence kinds, distinguished by the LLM's p_covers
(probability the control covers the CO), shown as "model confidence": missing_mapping: HIGH p_covers, but the CO is NOT mapped — the LLM
is confident the control covers it, so it should be mapped. Accepting
ADDS the mapping.
spurious_mapping: LOW p_covers, but the CO IS mapped — the LLM is
confident the control does NOT cover it, so the mapping is likely wrong
and inflates apparent coverage. Accepting REMOVES the mapping.
Only confident rows surface; the uncertain middle band is dropped. So a
~100%-confidence row is a strong "add" and a ~0%-confidence row is a strong
"remove" — both are actionable, in opposite directions.
Rows are sorted by confidence, so the strongest calls come first. Each
section is paginated: its pagination.filtered_total reports the full
count, so when it exceeds the rows returned, raise limit (up to 500) or
page with offset to review every divergence — not only the first page. Also returns group_sufficiency divergences (observation-only). Apply
coverage rows with resolve_verdict_divergences(action="accept"); set
aside rows the structural model got right with
resolve_verdict_divergences(action="dismiss"). |
| resolve_verdict_divergencesA | Accept a batch of coverage divergences as mapping changes, or dismiss
a batch of divergences. Mutating. action="accept": each missing_mapping ADDS its CO to the
control, each spurious_mapping REMOVES it, one version per affected
control. Items are validated one by one: the answer separates
applied from skipped (stale, would orphan, already so). To accept
only confident rows, filter get_verdict_divergence's coverage rows
by p_covers (near 1.0 for missing, near 0.0 for spurious) first.
reason (min 10 chars) is recorded on each control's history.
action="dismiss": the structural model was right and the LLM was
not; the model does not change. A dismissal is keyed to the row's
current verdict input, so the row reappears once its control or
objective changes. Works for coverage and group_sufficiency rows.
|
| list_compliance_frameworksA | List the compliance frameworks available to map controls against. Read-only; no side effects. Returns both built-in frameworks (e.g. OWASP
ASVS) and any custom frameworks in the workspace. Use this to discover
framework identifiers before select_compliance_frameworks (activate one
for a model) or import_compliance_framework (add a custom one). Takes no
arguments beyond the version guard. |
| import_compliance_frameworkA | Import a custom compliance framework. Requires PRO tier. Use this when your customer's program (regulatory, contractual, or
internal) is not covered by Mipiti's built-in frameworks. After
import, the framework is selectable on threat models exactly like
a built-in. Fields: name (required), version, description,
requirements (required, non-empty), level_definitions. Each requirement takes id and description (required), level
(integer, default 1), the optional grouping chapter_id /
chapter_name / section_id / section_name / title,
scope (component, the default, or system: covered if ANY
model satisfies it) and level_specific_text (per-level text). level_definitions and level_specific_text are keyed by the
level as a string integer ("1", "2"): the key is the ordinal the
level <= target_level filter compares, so a non-integer key is
refused (400). Labels ("Baseline", "SL3") go in each value's name.
A level value is {"name", "description", "source"}, where source
is authoritative (paraphrased from the published standard) or
mipiti_convention (tiers you defined).
Example:: {
"name": "ACME Tiered",
"level_definitions": {
"1": {"name": "Baseline", "description": "Minimum.",
"source": "mipiti_convention"}
},
"requirements": [
{"id": "ACME-PWD", "description": "Passwords meet policy",
"level": 1, "level_specific_text": {"1": "Min 8 characters."}}
]
}
|
| map_control_to_requirementA | Manually map one security control to one compliance-framework requirement. Mutating: records a control-to-requirement mapping, which re-derives that requirement's coverage in the compliance report. Use for a single, deliberate mapping you are asserting by hand. To let the LLM propose mappings across many requirements at once, use auto_map_controls; to close gaps end-to-end (map + exclude + fill), use auto_remediate_compliance. |
| auto_map_controlsA | LLM-map a model's existing controls to a framework's requirements. Requires PRO tier. Mutating: writes control-to-requirement mappings. Runs as a background job (typically 20-45s); this tool waits for completion and returns the result. Sits between the manual map_control_to_requirement (one mapping at a time) and the full auto_remediate_compliance loop (which also excludes non-applicable requirements and proposes new entities for remaining gaps). auto_map_controls only creates mappings from controls that already exist — it never adds or excludes entities. |
| update_organizationA | Set per-organization level grades for IEC 62443-4-1 and NIST CSF. Admin-only: the backend requires the caller to be an admin in the
organization (or a superadmin). Non-admins will get a 403; do not
invoke this tool unless you've verified admin role for the target
org. target_ml is the IEC 62443-4-1 Maturity Level the organization
targets for its secure-development program (1-5). csf_tier is
the NIST CSF Tier the organization targets for its cybersecurity
risk-management posture (1-4).
Because None on the wire is indistinguishable from "field
omitted", pass clear_target_ml=True or clear_csf_tier=True
to explicitly reset a value to NULL. Omitting both the value and
its clear_* flag leaves the existing server-side value
untouched. |
| add_componentA | Add a component to a threat model. Components bridge security architecture to code organization. They
map trust boundaries to repos so controls can be scoped to the
codebase that implements them. They also drive the deterministic
reachability composer's asset-boundary derivation: an asset's
trust-boundary footprint is the union of its components'
trust_boundary_ids. Generation reads no components, so add or edit them after
generate_threat_model, not before. A component with empty repo_url is either speculative (your own
code, not linked to a repo yet) or external (e.g. a third-party
service, the customer's IdP, or other external infrastructure you
call but don't own).
The component's trust boundary tells them apart: bind an
internal-zone component to its repo via edit_component; leave an
external-zone component unbound — its component_unbound finding
is a permanent external-dependency marker, not a gap to close.
Binding by "some client code touches it" is wrong: client code for
external dependencies lives in your repo too. |
| edit_componentA | Edit a component's properties. Per-component level grades are orthogonal axes — set whichever
apply to the program the component is in scope for. Leave a field
unset (None) to keep the current server-side value; backend
treats absent fields as "unchanged". |
| get_group_dependenciesA | The reliance edges among a tag's member models. Read-only; no side effects. Returns {tag_id, tag_name, models, edges, total}: one row per edge a
member declared on another model's control (manage_reliance /
attach_foundation), with both models' titles, the mode
(delegated or relied_upon), the source objective or control, the
provider control, its status (draft, active, broken,
rejected), validation_verdict and credit_state (whether it
credits its objective now). Empty when no member relies on another. Use it to review the cross-model dependencies of a product or audit
scope before an auditor export, or to find broken edges. A model's own
edges, in both directions, are list_reliance. |
| get_assertion_typesA | List the assertion types submit_assertions accepts, with their params. Read-only. Returns the catalogue as structured data: every type, what it
proves, its soundness class, which params it requires, which it
accepts (an array-valued param carries its item_schema), and a
worked example. soundness_classes defines the five classes by the
fact a pass establishes, weakest to strongest — presence,
under_approximating_scan, existential_witness,
sound_over_approximation, by_construction — and
sound_classes names the two that can credit a for-all clause.
covers gives the accepted form of a binding declaration. Call this before writing assertions. submit_assertions names the types and
their required params in its own description, but descriptions are prose a
client may present only in part, and a half-list reads exactly like a whole
one. This returns data, so what you get back is the complete contract. |
| submit_assertionsD | Typed claims about a control, assumption or functional test; CI checks later.
get_assertion_types returns it all as data. By class, strongest first, as name(required) [opt: optional]:
[by_construction] typed_boundary(scope, sinks, boundary_type, constructors, property) [opt: allowlist, wrappers]
[sound_over_approximation] sink_default_deny(scope, sinks, safe_forms, property) [opt: allowlist, wrappers]
[existential_witness] test_attested(test) [opt: env, mechanism]
[under_approximating_scan] pattern_matches(file, pattern) [opt: scope_start, scope_end, multiline, dotall, target] pattern_absent(file, pattern) [opt: scope_start, scope_end, multiline, dotall, target] no_plaintext_secret(file, patterns)
[presence] function_exists(file, name) class_exists(file, name) decorator_present(file, function, decorator) function_calls(file, caller, callee) import_present(file, module) file_exists(file) file_hash(file, algorithm, expected_hash, scope_file) [opt: scope_start, scope_end] config_key_exists(file, key) config_value_matches(file, key, pattern) env_var_referenced(file, variable) dependency_exists(manifest, package) dependency_version(manifest, package, constraint) parameter_validated(file, function, parameter) error_handled(file, function) middleware_registered(file, middleware) http_header_set(file, header) test_exists(pattern) module_exists(file, name) module_instantiated(file, parent, child) port_exists(file, module, port) [opt: direction] parameter_defined(file, parameter) [opt: module, pattern] signal_exists(file, name) [opt: module, kind] sva_assertion_present(file, name) register_reset(file, signal) [opt: reset]
Each: type, params, description, repo ("/" or "no_repo"), covers
beside them, never in params: the CO-NN or cls_ ids proved. A for-all clause
takes only typed_boundary (sinks accept one boundary type) or, when they do
not, sink_default_deny, bound with covers. |
| list_assertionsA | List active assertions for a control or assumption. Provide exactly one of control_id or assumption_id. Returns a flat list of assertions. Each assertion carries an origin
field: "own" for assertions submitted directly against this model's
control or assumption, "inherited" for assertions contributed through
model composition (composed models whose assertions apply here).
Inherited assertions are included in the listing. Each assertion also carries three INDEPENDENT verdict fields. Read them
together — a passing tier check is not the same as sufficient evidence: tier1_status — mechanical check: the named file, symbol, or
pattern is actually there. "pass" | "fail" | "pending".
tier2_status — semantic check: the cited code meaningfully
implements the claim. "pass" | "fail" | "pending".
coherence_status — advisory consistency signal across the
control's evidence set. "pending" here does NOT block the control
from verifying, does NOT mean a verdict is missing, and is NOT a
reason to trigger a recompute.
An assertion can pass BOTH tiers while its control stays unverified,
because verification is decided per CONTROL, not per assertion: a
control verifies only when its assertions collectively cover every
clause of the control description. Read get_sufficiency for that
verdict; never infer it from the tier fields here. Each assertion also carries covers (the objective or clause ids it
was declared to prove; empty when undeclared) and, where the platform
surfaces it, tier1_attested and evidence_provenance (whether
the run that verified it was signed and by what class of identity). |
| delete_assertionA | Permanently delete a single assertion from a control or assumption. Mutating and destructive: the assertion record is removed, not soft-deleted, and its contribution to sufficiency/verification is dropped. It does NOT itself re-run verification; the deletion queues a background re-evaluation of the control's sufficiency, which a later get_sufficiency read reports once it lands. Use to retract a claim that was submitted in error or that get_verification_report flagged as misaligned (off-topic for the control's current description). To add assertions use submit_assertions; to inspect them first use list_assertions. Only "own" assertions can be removed here — inherited assertions come from composed models and must be managed on their source model. |
| get_verification_reportA | Get verification report with summary stats and sufficiency gaps. Returns tier1/tier2 pass/fail/pending counts, per-control verification
status, and sufficiency details. Each per-control sufficiency block carries: status: "sufficient" | "insufficient" | "pending" | "stale".
"stale" means the stored verdict no longer reflects the current
control description, active assertion set or the rules it was
computed under. Reading does not queue a re-evaluation: the write that
changed a control queues its own. Call this tool again later for the
refreshed verdict.
details: human-readable LLM reasoning.
misaligned_assertion_ids: assertions whose stated subject is
off-topic for the control's current description (common after a
control has been refined or regenerated). Treat as a directive:
rebind to the right control, supersede via delete_assertion,
or rewrite. Do NOT treat them as evidence. A non-empty list forces
the verdict to "insufficient".
stale: boolean shortcut for status == "stale", kept distinct
so an INSUFFICIENT verdict that's also stale (the prior insufficient
decision was computed under outdated inputs) can be flagged without
overloading status.
A drift item means the accepted evidence changed (a test's definition,
a witness's scope or allowlist) and its verdict was withdrawn until
reviewed again. By default returns summary only (no per-assertion details). Set
summary_only=False to include full assertion details and drift items. |
| get_sufficiencyA | Whether the submitted assertions of one control, or of one functional
test, together prove it. Read-only; name exactly one id. For a control this explains verification_status: "partially_verified". status is sufficient | insufficient | pending, with freshness (fresh | stale | pending) beside it;
insufficient carries details naming EACH uncovered clause and the
evidence that would close it. A soundness_tier is the weakest
clause's tier: a control is proven no more strongly than its thinnest
clause. It is composed over the evidence as it stands now
(strength_basis: now), beside soundness_tier_at_evaluation, and
clause_strength lists each clause's tier now and when judged, with
each piece of evidence marked passes_now: relay a clause whose
evidence no longer passes. Reading does not queue a re-evaluation. For
the whole model use get_verification_report. commitments is what it was checked against: a person's declaration
(basis: "declaration"; an open cross-check finding keeps it
insufficient: get_control_delta) or a reading of the text.
Act by submitting the named assertions; a clause describing a mechanism
the system does not use calls for refine_control, not evidence.
get_control_work_order serves the per-clause list: where the order
names a required class for a clause, required_evidence carries the
clause id for covers and a suggested_submission skeleton whose
<...> placeholders you replace. For a for-all clause prefer
typed_boundary, else sink_default_deny. class_mismatch means
the bound evidence is the wrong CLASS and more of it will not help. An
attestation covers an existential clause, never a for-all one; that
clause's only other exits are a risk acceptance or a not-applicable
disposition. For a functional test it is whether its evidence proves the objectives
it is associated with, with the reasoning; it can read pending or
absent until evidence is submitted. |
| submit_findingsA | Record negative findings (gaps discovered while scanning a codebase against a model's controls). Mutating: persists new finding records against the model. Use after a gap-discovery scan (see get_scan_prompt) to log where expected control evidence was NOT found. Findings are the negative counterpart to assertions (positive proof via submit_assertions): a finding says "I looked here for this and it was missing." Once submitted, drive a finding through its lifecycle with update_finding and review them with list_findings. |
| list_findingsA | List negative findings recorded on a threat model. Read-only. Returns finding rows with their lifecycle status; use to triage gaps or to find a finding_id for update_finding / remediate_finding. Each row carries an origin ("own" for findings recorded on this model, "inherited" for findings contributed through model composition, with inherited_from_* context); inherited findings are included in the listing. |
| update_findingA | Advance a finding through its lifecycle. Mutating: updates the finding's status and metadata. Use to acknowledge, remediate, verify, or dismiss a finding previously recorded by submit_findings / list_findings. This records a MANUAL status transition — the machine-set auto_resolved state is not among the statuses it accepts; for gaps whose kind has an automatic fix, remediate_finding performs the actual cleanup instead. |
| remediate_findingA | Preview, and on confirmation apply, the platform's remediation of a
finding. Without apply it is read-only: it returns a structured diff of what
the remediation would change, shaped by the finding's kind (for
structural_duplicate_controls: which controls are kept, which
dropped, and the CO mappings and framework refs the survivor takes).
Show the operator that diff and get explicit confirmation. With apply=True it commits the change, recording justification
(a one-line operator rationale, required) on the audit trail. Never
apply without having shown the preview: the platform records who acted
but does not enforce the preview — the agent does. 404 when the finding does not exist; 422 when its kind has no automatic
remediation (resolve those with the control tools); applying a finding
already remediated or dismissed is refused (409). |
| get_findings_risksA | Workspace-scoped triage dashboard: open findings, active risk
acceptances, and at-risk Control Objectives across every model
the workspace can access. Use this as the entry point when an operator asks "what's open?"
or "what should I work on next?" — one round-trip returns all
three categories with model context and risk dimensions
(severity, status, risk_tier, owner, review_by) so the agent can
triage without per-model fan-out. The endpoint is read-only and
fast; it composes from existing per-model queries server-side. Returns the envelope verbatim: {workspace_id, evaluated_at, models, findings, risk_acceptances, at_risk_cos, summary}.
summary carries totals (open_findings, total_findings,
active_risk_acceptances, total_risk_acceptances,
at_risk_cos) for quick health-check responses. |
| get_remediation_leverageA | Remediation-leverage plan for a model: which controls to implement
first to close the most control objectives with the least work. Returns the model's not-yet-satisfied controls ranked by how many
control objectives each one closes (ranked), plus a greedy
minimal fix order — the sequence of controls that reaches the most
mitigated objectives with the fewest controls (greedy_plan) — and
a summary of the collapse (total objectives, currently mitigated,
how many controls the plan needs). Use to prioritize implementation
work: a single call tells the agent which controls give the highest
leverage, so it can tackle the shortest path to coverage instead of
fixing objectives one at a time. Read-only. Composed models: each entry in ranked and greedy_plan also
carries its owning model — owner_model_id and owner_model_title
— and an inherited flag. inherited is true when the control is
authored on an ancestor model, meaning the fix lands on that model
rather than the one being assessed; summary.inherited_candidate_controls
counts them. Surface the owning model so the operator knows which
high-leverage fixes belong to a parent model. A flat (non-composed)
model reports every control as owned by the assessed model. |
| list_risk_acceptancesA | List all risk acceptances on a specific threat model — risks
that an operator explicitly accepted instead of mitigating. Each entry carries the CO id, owner, justification, status
(active / expired / revoked), and the review
deadline. Use to inspect which gaps were intentionally accepted
versus genuinely unaddressed when triaging at-risk COs. Returns risk acceptances ONLY. An objective declared not applicable is a
different claim — it is not an accepted risk, and counting it as one would
read a "does not apply here" as "we are carrying this exposure". Use
list_co_dispositions to see those, or both together. |
| create_risk_acceptanceA | Record that an operator explicitly ACCEPTS the residual risk on a control
objective instead of mitigating it — the write counterpart to
list_risk_acceptances. Use when a control objective's residual risk is a deliberate, documented
decision rather than an unaddressed gap: the acceptance carries an owner, a
justification, and a review deadline, and reads as active until it
expires or is revoked. Prefer this over leaving a known-and-accepted risk
implicit — it makes the decision auditable and forces a revisit by the
deadline. An accepted objective is still surfaced (as accepted, not
unaddressed) when triaging at-risk objectives. |
| create_co_dispositionA | Record that a control objective DOES NOT APPLY to this system — a signed,
expiring judgment, not a dismissal. The sibling of create_risk_acceptance, and the distinction between them
is the claim being made. A risk acceptance says the exposure is real and we
are carrying it. A disposition says this objective does not apply here at
all — the asset is not handled the way the objective assumes, the attacker
position does not exist in this deployment, the capability is not present. The objective is not removed. It stays in the control-objective matrix,
stays in every coverage count, and is reported in its own class alongside
the owner and justification recorded here. That is the point: a reviewer can
see the judgment and challenge it. An objective that simply vanished would
be indistinguishable from one nobody modelled. What it does change is work: no controls are generated for the objective and
no coverage gap is raised against it, because an objective that does not
apply is not a gap. review_by is required and is not a formality — the claim stops applying
on that date, and the objective returns to whatever posture its controls
give it, gap included. Choose a date by which someone can realistically
re-check that the claim still holds.
Use create_risk_acceptance instead when the objective DOES apply and the
exposure is being carried deliberately. If an objective is only unaddressed
rather than inapplicable, neither tool is right — add controls. |
| list_co_dispositionsA | List the signed judgments recorded against this model's control
objectives — risk acceptances, not-applicable dispositions, or both. Read-only. Each entry carries the objective it names, the owner who signed
it, the justification, the dates, and its status. Expired and revoked
entries are included: a decision that lapsed is part of the audit trail, and
hiding it would leave a reader unable to tell a judgment that was reviewed
from one that was never made. Read this before authoring a new judgment on an objective — an existing one
may already cover it, or may have expired and need re-signing rather than
duplicating. |
| complete_setup_stepA | Mark one onboarding setup step as done. Mutating: updates the workspace onboarding checklist. Call after actually performing the corresponding setup action on the user's behalf. Check current progress with get_setup_status first to avoid re-marking completed steps. An unrecognized step_id is rejected without any state change. |
| get_setup_statusA | Get the workspace onboarding checklist with completed and pending steps. Read-only. Call this before suggesting or performing setup actions so already-done steps aren't repeated; mark a step done with complete_setup_step. Takes no arguments beyond the version header. |
| add_trust_boundaryB | Add a trust boundary. Creates a new model version. Returns {model, entities, ...}: the model's id, version and title, and
entities.trust_boundaries the boundary as created. |
| edit_trust_boundaryA | Edit a trust boundary. Creates a new model version. Returns {model, entities, ...}: the model's id, version and title, and
entities.trust_boundaries the boundary as it now stands. Attesting the seal is a judgment: a person's edit attests it; a
program's only under a workspace rule delegating seal_attested,
otherwise the edit lands with the seal unattested and the answer's
attestation names the parked escalation_id (never a 403). Any
change to passes, crosses, neighbors or sealed voids an
attested seal: the attestation is of the content as it stood. |
| add_assumptionA | Add an assumption. Creates a new model version. Returns {model, entities, ...}: the model's id, version and title, and
entities.assumptions the assumption as created. Assumptions represent security properties outside the system owner's
trust boundary. When linked to COs and attested, they mitigate those
COs in the assessment. Optionally attach a structured exclusion predicate (the
exclusion_* params). The reachability composer matches active attested assumptions with predicates against COs deterministically
— class-3 (deterministic computation) evidence in addition to the
operator-attested class-1 evidence. Pass any subset of the fields;
unspecified fields default to wildcard ("*"). When
exclusion_co_ids is non-empty, it takes precedence over the
match fields.
Use this to resolve a CO whose composer verdict is
indeterminate because no structural primitive backs an
operator non-applicability claim: set exclusion_co_ids=<co_id>
(and optionally the attacker/asset/property fields), and the
composer will derive unreachable / reason: assumption_excludes
on subsequent loads, with the assumption's structured predicate as
the audit-trail cause. |
| edit_assumptionB | Edit an assumption. Creates a new model version. Returns {model, entities, ...}: the model's id, version and title, and
entities.assumptions the assumption as it now stands. |
| submit_attestationA | Record that a responsible party affirmed an assumption holds. Only for external assumptions. Non-applicability assumptions
require CI verification (submit assertions + run mipiti-verify) — manual
attestation is rejected for them. An assumption with a current attestation can mitigate linked COs.
When the attestation expires, those COs become at-risk until
re-attested or covered by controls. Attesting accepts the assumption, which is a judgment about the world
the platform cannot check: a program may do it only under a workspace
delegation rule for assumption_accepted; otherwise the call is
refused with HTTP 403 and an escalation_id and the attestation is
parked for a person. An attestation holds for the text it was given
for: editing the assumption's description retires it, and the
assumption must be accepted again. An assumption need not be linked to
an objective to be accepted — one bound into a control's group (see
strengthen_controls) counts only while it is accepted. An attestation is a responsible party's claim, never a proof over every
site: it can cover an existential clause of a control (its tier reads
claimed) and never a for-all one, where only a sound witness
counts. An attestation the platform mints from CI results is no
stronger than the weakest assertion behind it. The exits for a
universal objective that cannot be proven are a risk acceptance or a
not-applicable disposition. |
| list_attestationsA | List an assumption's attestation history. Read-only; no side effects. Returns the chronological record of attestation events recorded against the assumption (each with its actor, timestamp, and status/expiry as recorded), so you can trace why the assumption is currently attested, expired, or never attested. An assumption only mitigates its control objectives while it is active AND currently attested, so use this to diagnose coverage that depends on an attestation. To record a new attestation use submit_attestation; for the assumption's current fields (status, description) use get_entity (entity_type="assumption"). |
| get_control_assumption_groupsA | Get the current assumption group structure for a control. Assumption groups define alternative sets of external claims that can
satisfy a control: |
| set_control_assumption_groupsA | Declaratively set the assumption group structure for a control. Replaces all assumption group assignments for this control. Each group
is a set of assumption IDs that together externally handle the control;
any one group being fully active+attested is sufficient. Within a group: AND — all referenced assumptions must be active and
attested for the group to count as complete Across groups: OR — any one complete group marks the control as
externally handled for mitigation purposes
To clear all assumption groups (revert to "not externally handled"),
pass an empty JSON object: {}. AI relevance gate (per group, no override):
Each non-empty proposed group is evaluated independently. The behavior
depends on how many groups pass: All groups accepted → 200 success, structure persisted as submitted. Some groups accepted (partial): the accepted groups ARE persisted
(runtime OR-semantics activate immediately), the rejected groups
are NOT saved, the call raises with HTTP 422 detailing both
persisted_groups and rejected_groups (with per-group reasoning).
Resubmit only the rejected groups with assumptions that cover the
control, or sharpen those assumptions' descriptions. All groups rejected: existing groups on this control are
re-evaluated through the same gate. Relevant existing groups are
preserved; irrelevant existing groups are dropped (assumptions
themselves remain in the model — only this control's linkage is
removed). The call raises with HTTP 422 detailing what was
persisted, what was rejected, and what existing was dropped. Empty submission ({}): clears all groups, no evaluation.
There is no force-override. To get a group accepted, choose assumptions
whose descriptions actually cover the control or refine an assumption's
description so coverage is explicit. |
| convert_assumption_to_controlsA | Convert a violated or retired assumption to controls. Mutating. Retires the assumption's CO linkage and, when any CO it covered is left
with no control, proposes a control build for those COs: the result's
proposal (null when nothing is owed) is started with
start_control_build after review, and authors the controls then.
Nothing is authored by this call. Use when an assumption is no longer
valid and the system owner needs to implement controls instead. Side effect on control-level linkage: this assumption is also removed
from every assumption_groups entry on every control that referenced it.
Any group left empty by the removal is dropped; a control's status is
not changed, and a control left with no group is no longer backed by
an assumption. Underlying assumptions are not deleted — only the
linkages. |
| generate_functional_objectivesA | Derive capabilities, functional objectives, and the concrete tests to
implement from the feature spec. Capabilities are the behaviours the feature must deliver; each is walked
against a taxonomy of operating conditions (nominal, boundary, invalid
input, dependency failure, concurrency, …) to produce testable
Given-When-Then objectives — and then a concrete, implementable test is
specified for each objective (so the agent implements the tests rather than
deciding what to test). Requires a Pro plan. Billable — may take some time.
refresh=true re-derives from scratch, replacing prior generated (not
manually authored) capabilities, objectives, and tests. |
| get_capabilitiesA | A model's capabilities (behaviours the feature must deliver), or one
of them. Read-only. Without capability_id: every capability, each with its id,
name/description and a summary of its component and asset bindings.
With it: that capability with its bound components and assets. |
| get_functional_coverageA | A model's functional coverage report, or just its actionable gaps.
Read-only. By default the whole picture: per-objective state (verified / covered /
failing / untested), the Capabilities × Conditions matrix, and the
applicable / missing-objective / not-applicable cell accounting. With
gaps_only=True only what needs action: applicable conditions with no
objective yet, and objectives that are failing or have no passing test. |
| add_functional_testA | Hand-author a single functional test and map it to one or more objectives. Mutating. Generation (generate_functional_objectives) already specifies the tests to implement, so use this only to register an extra test that generation did not produce; a manually-added test survives regeneration/refresh. For bulk-registering tests that already exist in your codebase, use import_functional_tests instead. This records the test at the status you claim — it does not run or verify anything; CI verification happens only when you attach TEST_EXISTS/TEST_ATTESTED evidence via submit_assertions with functional_test_id. |
| import_functional_testsA | Register tests that already exist in your codebase against a model's functional objectives, so tests you already have count toward functional conformance — not only Mipiti-specified tests. Mutating (bulk). Scan the repo's test suite and pass the tests here. Optionally associate each with the objective ids it covers (from get_functional_objectives); the platform verifies each association is applicable before accepting it and returns any it rejected under rejected_mappings. A test with no (or a rejected) association is still imported, unmapped, so it can be associated later (see suggest_functional_test_mappings / associate_functional_test). For a single hand-authored test, use add_functional_test instead. |
| suggest_functional_test_mappingsA | Suggest which functional objectives each imported test likely covers. For unmapped tests (imported without an association, or added without
objective ids), this proposes objective mappings so you can review and
apply them with associate_functional_test. It only suggests — nothing is
associated until you confirm. |
| associate_functional_testA | Associate a functional test with one or more functional objectives. Use this after suggest_functional_test_mappings, or to hand-map a test to
the objectives it covers. The platform verifies each association is
applicable before accepting it and returns any it declined under
rejected_mappings. |
| get_functional_satisfaction_groupsA | Read the satisfaction-group structure for a functional objective. Read-only; no side effects. A satisfaction group is a set of functional tests that together satisfy the objective: AND within a group (every test in the group must be verified), OR across groups (any one complete group satisfies the objective). Returns the current numbered groups plus any tests associated with the objective but not placed in a group. Use before set_functional_satisfaction_groups to see the current structure, or to trace why an objective is / isn't satisfied. This is the functional analog of get_control_assumption_groups / get_mitigation_groups. |
| set_functional_satisfaction_groupsA | Declaratively set (replace) a functional objective's satisfaction groups. Mutating. Replaces the objective's group structure wholesale. Each group is a set of functional tests that together satisfy the objective (AND within a group); the objective counts as satisfied when any one complete group has all its tests verified (OR across groups). Tests you want to keep associated with the objective but outside any group go in ungrouped. Unlike set_control_assumption_groups, there is no AI relevance gate — the structure you submit is applied as-is. Read the current state first with get_functional_satisfaction_groups. |
| get_cwe_catalogA | Get the platform's CWE reference catalog status. Returns {enabled, current_version, entry_count, versions}. When CWE
classification is not turned on for this instance, enabled is false
and the rest is empty — this is a normal informational response, not an
error. |
| get_model_cwe_tagsA | List CWE weakness classifications tagged onto a model's control objectives. Each tag's name/description are resolved from the platform's CWE catalog,
never model-authored. A tag whose CWE id has since been deprecated,
redefined, or removed by MITRE carries a stale reason (missing /
deprecated / changed) — re-run classify_model_cwe to refresh
it. 404s if CWE classification is not enabled on this instance. |
| classify_model_cweA | Classify a model's control objectives against the platform CWE catalog. Grounded: the model may only select from the catalog's current-version
candidate ids, and every returned id is re-validated against the catalog
before storage — a hallucinated or deprecated id is never persisted.
Skips control objectives already tagged at the catalog's current version
unless force is set. Returns a summary:
{status, catalog_version, cos, classified, tags_written, skipped}.
404s if CWE classification is not enabled on this instance. |
| get_entityA | Get a single entity of any core type by ID. Read-only. Dispatches on entity_type to the per-type read and returns that
type's native record as-is (not wrapped in an array): asset — the asset's typed fields. Soft-deleted assets carry
deleted: true; the caller decides whether to surface them.
entity_id e.g. A-01. A live asset or attacker also carries
quality: the quality check's outcome (clean, advice,
defect, question, invalid, or unchecked while none
answers it as it stands), the warning, the proposed correction
or the question, and the input_hash that
apply_entity_proposals and answer_entity_question name.
attacker — the attacker with its factor decomposition, its
surface_extent (unset / point / whole) and
surface_extent_source, which says whether a person attested it.
Soft-deleted attackers carry deleted: true. entity_id e.g.
T-03.
component — the component. Speculative components
(repo_url="") are returned as-is: the empty repo IS the
lifecycle state, not an error. entity_id e.g. CMP-01.
trust_boundary — the boundary incl. its passes set
(closed-vocabulary subset of
{Network, Adjacent, Local, Physical}). entity_id e.g.
TB-Net.
assumption — the assumption with its override applied (mirrors
list_assumptions' merge for one entity: typed fields, the
structured exclusion predicate when present, and the override
layer — status / justification / linked CO IDs).
Soft-deleted assumptions carry deleted: true. entity_id
e.g. AS-01.
|
| remove_entityA | Soft-delete a single entity of any core type. Mutating: creates a
new model version. Reversible with restore_entity using the same
entity_type — the entity's ID is preserved (never reused) so a
restore reinstates the same ID and all its links. To change an
entity's fields instead of removing it, use the typed edit_* tool. Runs as a background job, reporting progress while it waits; a job a
client stops waiting for finishes or fails on its own. A removal that
is refused (the entity is not found or already deleted, or it is the
last live asset or attacker) fails with the reason. Dispatches on entity_type. Per-type consequence (all derived at
read time; nothing is hard-destroyed): asset — the asset's (asset × attacker) CO pairs are tombstoned,
orphaning any controls mapped to them.
attacker — control objectives anchored to this attacker are
tombstoned; controls left with no live anchor become orphaned.
component — controls scoped to this component have their
component_id cleared (the controls themselves are kept) and the
component's trust-boundary contribution to asset reachability is
withdrawn.
trust_boundary — reachability widens: attacker vectors the
boundary was filtering now pass freely and its sealed/isolation
claim is dropped, so CO reachability verdicts past it can flip
toward reachable/indeterminate.
assumption — marked deleted (kept for the audit trail); its CO
links are cleared and its attestations retired; controls whose
assumption_groups name it keep their groups, which credit
nothing through it while it is deleted.
Returns {model, entities, controls_carried, controls_orphaned, orphaned_control_ids}: the model's id, version and title, and the
entity as removed. |
| restore_entityA | Un-soft-delete a single entity of any core type, reversing a prior
remove_entity. Mutating: creates a new model version. Only affects
an entity that is currently soft-deleted. Dispatches on entity_type. Per-type effect: asset — revives the asset's tombstoned (asset × attacker) COs
with their original IDs, un-orphaning any linked controls.
attacker — reinstates the attacker under its original ID,
revives the COs tombstoned when it was removed, and un-orphans any
controls that were anchored to it.
component — reinstates the component under its original ID,
restoring its trust-boundary contribution to asset reachability.
trust_boundary — reinstates the boundary: the reachability it
filtered re-narrows and its sealed/isolation claim is restored,
so CO reachability verdicts past it can flip back toward
unreachable.
assumption — returns the assumption to active status; controls
whose assumption_groups referenced it keep their group
structure intact. Its CO links are not restored (set them again
with edit_assumption), and re-attestation is required before it
counts anywhere it is linked or grouped.
Returns the entity-change result: {"model": {id, version, title}, "entities", "controls_carried", "controls_orphaned", "orphaned_control_ids", ...}, entities holding the entity as
restored. |
| get_risk_viewA | Prioritized Risk View — one row per live Control Objective — at a chosen scope. Read-only; no side effects. scope selects the aggregation boundary and how scope_id is interpreted:
"model" — a single threat model (scope_id = model id). One row per live CO with derived risk tier, asset impact, attacker likelihood, control coverage counts (coverage_ratio), and open-finding count (open_findings). Tombstoned COs are excluded; pair with get_threat_model if historical context is needed. Use to triage which COs need attention on one model — a single call ranks the work, no per-CO fan-out.
"tag" — every member model of a tag (scope_id = tag id). The same row shape with model_id and model_title added per row, so rows can be grouped by source model without an extra lookup, and delegation-aware (delegation_mitigated / delegating_controls): a CO mitigated via a verified cross-model delegation reads as covered, consistent with each model's own assessment. Use for a product, portfolio or audit-scope posture rollup.
|
| get_compliance_reportA | Compliance gap-analysis report for one framework at a chosen scope. Read-only; no side effects. Requires PRO tier. Evaluates every framework requirement against the mapped controls in scope and classifies each as covered, partial, uncovered, unmapped, or excluded, then returns coverage counts plus per-requirement rows. The framework must first be activated at the same scope via select_compliance_frameworks (with the matching scope), otherwise there is nothing to report on. scope selects the boundary and how scope_id is read:
Filtering / pagination: level — level filter for level-aware frameworks; returns only requirements at or below this level (e.g. 1 for L1 only). Omit (or 0) for all levels. Honored for all scopes.
status — one of "covered", "partial", "uncovered", "unmapped", "excluded"; empty = all statuses. Model scope only.
offset / limit — per-requirement row pagination; offset skips the first N rows, limit caps rows returned (0 = no explicit limit). Model scope only.
A tag report is neither paginated nor status-filtered; passing status, offset, or limit with scope="tag" raises an error rather than silently returning unfiltered rows. |
| select_compliance_frameworksA | Select (activate) compliance frameworks at a chosen scope. Requires PRO tier. Mutating. Discover valid ids with list_compliance_frameworks (or add a custom one via import_compliance_framework); view the resulting gap analysis with get_compliance_report at the same scope. Re-calling replaces the scope's framework selection. scope selects the target and how scope_id is read:
"model" — a single threat model (scope_id = model id). Activating a framework also kicks off background auto-remediation: it auto-maps existing controls to requirements, excludes non-applicable requirements by taxonomy, and suggests/applies new entities for the remaining gaps. The response includes auto_remediate_jobs, which run and complete on their own; re-trigger later with auto_remediate_compliance if the model changes.
"tag" — a tag (scope_id = tag id). Records the frameworks against the tag AND propagates them to every member model, and to every model added to the tag later, making the tag a compliance scope (e.g. an audit boundary) spanning several models.
|
| export_reportA | Export a threat model or a tag as a downloadable document. Read-only; no side effects on the source. scope selects what is exported and how scope_id is read; format selects the representation:
scope="model" (scope_id = model id) supports format ∈ {csv, pdf, html, archive}:
csv — the model's current state rendered as CSV; returned inline as UTF-8 text in content.
pdf / html — rendered document returned base64-encoded in content_b64 (with content_type). Runs as a server-side job; progress is reported automatically while it completes, which may take time for large models.
archive — the self-contained, independently-verifiable JSON audit bundle of the model's current state: its latest version and controls, live assertions (with Tier 1 / Tier 2 verdicts and attested flags) and the runs behind them, open findings and those a person closed, risk acceptances and other decisions in force, assumption overrides, attestations, and instance sufficiency signatures; each control's per-clause evidence basis travels with it. Earlier versions, activity and chat are not in it. Those verdicts are the origin's record of what it claimed, which is what a third party checks against the signatures; an importing workspace credits what its own verification establishes (see import_threat_model_archive). Returned as {..., "envelope": <dict>}; feed the envelope to import_threat_model_archive to restore it into any workspace. Model scope only.
scope="tag" (scope_id = tag id) supports only format="html": the signed auditor report, every member model's report after the reliance edges among the members (each with its status and whether it credits its objective), in one HTML document returned inline in content. csv, pdf, and archive are rejected for tag scope.
|
| list_groupsA | List the workspace's tags. Read-only; no side effects. A tag is a named, overlapping grouping of threat models, for audit
scopes, products, ad-hoc selections or portfolios. A model may carry many
tags, and a tag never affects posture or credit. Returns {"tags": [...]},
each with id, name, description and model_ids. Discover tag IDs here before the tag risk, compliance, dependency or
export tools, or before adding/removing members. For a single model's
tags use list_model_groups. |
| create_groupA | Create a tag, optionally with its first members. Mutating. A tag groups models for viewing and reporting without asserting any
relationship between them and without moving credit. Names are unique
within the workspace (409 on a clash). Every model named in model_ids
must be one the caller can access in this workspace. |
| add_model_to_groupA | Add a threat model to a tag. Mutating. Links the model into the tag without moving or copying it; the model
stays independently editable, and may belong to many tags. The model
takes on the compliance frameworks the tag selected, marked as the
tag's, so removing a framework from the tag removes it from the model
again; a framework the model selected itself is left as it is. |
| get_groupA | Get one tag by ID, with its member model ids. Read-only; no side effects. Returns {id, workspace_id, name, description, created_at, model_ids}.
Discover tag IDs with list_groups. |
| delete_groupB | Delete a tag. Mutating; the member models are not affected. The tag's framework selections, requirement exclusions and relevance
data are deleted with it. Frameworks it propagated to its members stay
selected on them. |
| remove_model_from_groupA | Remove a model from a tag. Mutating; the model itself is not deleted. The frameworks the tag propagated to the model stay selected on it. |
| list_model_groupsA | List the tags a given model belongs to. Read-only; no side effects. A model may belong to many tags. Returns {model_id, tags: [...]}. Use
list_groups for every tag in the workspace. |
| get_reachability_verdictsA | Per-CO reachability verdicts, over this model alone or the composed
tree. Read-only; derived each time, never stored. composed=False (default): derived from this model's own structure
(components, asset.component_ids, trust_boundary.passes, each
attacker's trust_boundary_ids and vector, assumption exclusion
predicates), deterministic, the derivation an auditor re-runs.
co_id returns one verdict (404 if absent or tombstoned). Returns
{model_id, model_version, verdicts: [{co_id, kind, reason, narration, boundary_id?, assumption_id?}]}; kind is reachable,
unreachable or indeterminate.
composed=True: the same derivation over the model with everything
it inherits from its ancestors, for a child on the composition tree.
Paginated (page, page_size); kind_filter keeps one kind;
co_id is ignored. Returns {model_id, flag_enabled, verdicts: [{co_qid, asset_qid, attacker_qid, kind, reason}], total, page, page_size}, empty with flag_enabled: false where composition is
not available.
An indeterminate verdict names the missing structure:
attacker_unpositioned (edit_attacker with
trust_boundary_ids), asset_unbounded
(assign_to_components(target_type="asset")), no_shared_boundary
(reposition the attacker, rescope the asset, or an add_assumption
exclusion), missing_entity (restore it, or remove the CO).
model_coherence_report presents the same gaps as findings. |
| recompute_verdictsA | Estimate, force, or retry a model's verdict evaluation. mode="quote" (default): read-only. The cost of a recompute,
enqueueing nothing: {estimated_credits, computed_at, rate_version, informational, total_enqueueable, already_evaluated, governor}.
Subjects already carrying a verdict cost nothing, so it is an upper
bound. Show the operator this number before recomputing: on a large
model it runs to thousands of credits.
mode="recompute": mutating. Queues a fresh evaluation of every
control's coverage verdict and every live objective's
group-sufficiency verdict, bypassing quiet-period batching; usage is
metered as it runs. Returns {model_id, model_version, enqueued_coverage, enqueued_group_sufficiency, total_enqueued, estimated_credits, quote, governor}.
mode="retry_parked": re-runs only the verdicts a transient failure
(outage, exhausted credits, timeout) parked, of every kind, including
per-control sufficiency and coherence; nothing else is touched.
Returns {model_id, model_version, retried_slots, governor}.
Work runs in the background; when governor.exhausted it is queued and
resumes at governor.resets_at, never dropped. A recompute evaluates COVERAGE and GROUP SUFFICIENCY only. A control at
partially_verified, or coherence_status: "pending" on an
assertion, is not a reason to recompute: that verdict is computed on
assertion write and read with get_sufficiency. Recompute when
control-to-CO mappings look wrong (get_verdict_divergence). One
objective awaiting judgement is judge_objective. |
| judge_objectiveA | Have one control objective's mitigation group judged. Mutating —
queues background work and may consume credits. Use this for an objective whose risk_reason is awaiting_judgement:
it has a built mitigation group and nothing has decided whether that group
covers the objective — never evaluated, evaluated against inputs that have
since changed, or the answer parked. The objective is not short of
controls, so generating or implementing more will not move it; what is
missing is the judgement. This is not a repair. The judgement can come back insufficient, which
moves the objective to coverage_gap / insufficient_by_design and
names real work. That is the tool doing its job: it replaces "nobody has
looked" with an answer, and the answer may be no. Scoped to ONE objective, which is the difference that matters against
recompute_verdicts: that tool force-enqueues every control's coverage
verdict AND every live objective's group-sufficiency verdict, which on a
large model runs to thousands of credits. This queues a single judgement.
Any credits it consumes are metered at actuals as the work runs, like
every other metered call — the account's usage is visible in its billing
panel before and after. Judging runs in the BACKGROUND; the call returns as soon as the work is
queued. Re-read get_mitigation_groups (or assess_model /
get_risk_view) shortly after to see the objective's new state. Calling
again while a judgement is already queued is harmless and does not queue a
second one. A refusal comes back as data rather than an error, so it can be relayed: {queued: false, http_status: 409, ...} — controls are still being
generated for this model. Poll get_control_generation_status until
terminal, then call again.
{queued: false, http_status: 503, ...} — judging is unavailable on
this deployment.
{queued: false, http_status: 402, code, message} — the balance this
workspace bills to cannot cover the judgement.
|
| list_reconciliation_candidatesA | Entities a child model authored that look like ones it inherits.
Read-only. Use on a child model in a recursive tree to find duplicates before they
distort coverage; decide each with decide_reconciliation_candidate. disposition="active" (default): the open queue, paginated
(page, page_size). {model_id, flag_enabled, total, tiers: {certain, heuristic}, page, page_size, candidates: [{kind, own_qid, inherited_qid, tier, reasons}]}. Tier certain is a deterministic
match, safe to apply; heuristic is a fuzzy name/description match
that needs review. Rejected pairs are left out.
disposition="rejected": the pairs recorded as NOT duplicates, oldest
first and not paginated: {model_id, flag_enabled, rejections: [{id, model_id, kind, own_qid, inherited_qid, rejected_by, rejected_at}]}.
An id is what an unreject names.
Where composition is not available both come back empty with
flag_enabled: false. |
| undo_composition_eventA | Preview, and on confirmation apply, the undo of a lift or split. event_type is lift or split; event_id is the forward
lift_applied / split_applied activity event's id, or the
lift_id / split_id in its payload. model_id is the model the
event was raised on (another model's event is 404).
By default (dry_run=True) it is read-only: {plan, refusal}, exactly
one non-null. plan lists the inverse operations an undo would commit
(lift: tombstone the LCA entity, restore the source copies, rewrite CO
references; split: restore at the ancestor, tombstone the target
copies). refusal lists why it cannot: state has moved since the event
(assertions submitted on the entity, objectives referencing it, an
edit). Show it to the operator. With dry_run=False it is mutating, after explicit confirmation: the
divergence check runs again (409 with detail.refusal.reasons when it
refuses), the inverse is persisted across every affected model, and a
lift_undone / split_undone event citing original_event_id is
recorded. Returns {undone_event_id, original_event_id, applied_state_ops, models}; models is {lca_model, source_descendant_models} for a lift and {ancestor_model, descendant_models} for a split, each model its id, version and title. 503 where composition is not available. |
| get_functional_objectivesA | List a model's functional objectives, or fetch one by id. Read-only; no side effects. A functional objective is a Capability × Condition test plan expressed as a
Given-When-Then statement. functional_objective_id selects the behaviour: omitted / empty string -> list every functional objective for the model
(the full functional test plan). a functional-objective id -> return just that one objective's detail,
including its capability, condition, Given-When-Then statement, and
current test state.
For pass/fail coverage state across all objectives use
get_functional_coverage; for the actionable gaps pass it
gaps_only=True. |
| get_controlsA | A model's controls, or one control. Read-only. Without control_id: {controls, total, returned}, the published
set, filtered by status, co_id and component_id and paged by
offset / limit. Orphaned controls (every mapped CO tombstoned)
are left out unless include_orphaned=True, soft-deleted ones unless
include_deleted=True. summary_only=True returns only id,
description, status, verification_status, assertion_count, co_ids,
assumption_groups and attestation_dependency. An empty list on a new
model means its proposed build was never started
(get_control_generation_status); while a build holds the model the
list is the last published set, with a building marker. With control_id: that control, with an orphaned flag; version
reads it as of a model version (0 = latest). An objective id on a control is an ATTACHMENT, not credit: only a member
of a required mitigation group earns any, and a control that is
defense-in-depth everywhere can be implemented and verified without
moving an objective. Read get_mitigation_groups before evidence work. status is the operator's (not_implemented / implemented /
verified); verification_status is the EVIDENCE: verified
(both tiers pass and the assertions cover the whole description),
partially_verified (a tier failed, clauses are unproven, or an
attestation EXPIRED — get_sufficiency says which; the fix is more
assertions or a narrower description, never a recompute), pending,
unverified (none submitted). A for-all clause is credited only by a
sound type bound with covers; get_control_work_order lists what
each clause needs. status="implemented" finds what still needs
evidence. Only this model's own controls are listed; inherited ones are
counted by assess_model.
|
| get_control_objectivesA | Get the control objective matrix, or one control objective. Read-only. Two modes, selected by whether co_id is set: Matrix mode (co_id omitted) — returns the model's COs, each
with references to the controls that cover it. By default returns a
compact summary (total count only); pass offset/limit to
page through full CO records. Single mode (co_id set) — returns that one CO's typed
fields, the IDs of any controls that map to it, and the
deterministic reachability verdict (the structural derivation that
backs any reach claim on the CO). Tombstoned COs (removed: true) are returned with the flag set; the verdict is omitted
because reach state is frozen at the removal version.
offset/limit are ignored in this mode.
For pass/fail assurance scoring use assess_model. |
| get_objective_groupingA | What grouping a model's objectives by asset groups would do, before
it is done. Read-only: the groups are formed on a copy, and nothing is
written or judged. Assets that are the same for every objective (the same security
properties, impact, components, exposure and split knowledge) can be
judged together: one objective per group and attacker instead of one per
asset. Each asset keeps its id and stays in the inventory, marked with
its group. Returns grouped (whether the model's objectives are grouped now),
available (whether grouping may be turned on for this model),
groups (each group the restructure would form, by the id it would
keep, or new, with its members), objectives_now and
objectives_after, and model_version, the version
set_objective_grouping names. |
| set_objective_groupingA | Group a model's objectives by asset groups, or stop grouping them.
Mutating; creates a model version. Calls no model, so nothing is
estimated: the judgements the new version re-keys are queued as for any
edit. Read get_objective_grouping first, relay the groups and the
objective counts to the person, and pass the model_version it
returned: a model that moved since is refused 409 review_stale.
Grouping is refused 409 behavioural_off where
get_objective_grouping reports available false; stopping is
never refused for it. The write gate refuses it while a control build
holds the model. Grouping forms the groups from scratch; stopping deletes every group,
so each asset has its own objectives again. Either way the controls'
mappings move with the objectives (moved: each retired objective to
where its mappings went), each mitigation group landing as a group of
its own. A disposition on a retired objective does not carry over. |
| assign_to_componentsA | Replace an asset's or a control's component scope. Mutating. Components are the canonical bridge between security architecture
(trust boundaries) and code organization (repos). target_type
selects what is being scoped: "control" — replace a control's component scope. A control
scoped to one or more components is visible to coding agents
working in those repos (matched via Component.repo_url +
Component.path); an unscoped control is visible everywhere. Use
when wiring a previously unscoped control to the component(s) that
implement it, adding a second component to a cross-cutting control
(e.g. "all microservices enforce JWT validation"), or correcting a
wrong assignment. target_id is the control ID (e.g. "CTRL-03").
"asset" — replace an asset's component scope. Linking assets to
components flows boundary context into reachability derivation
without giving Asset its own trust_boundary_ids. Multi-component
is the right shape for a multi-instance asset (e.g., a session
token on client + cache + DB — each component handles a distinct
instance). target_id is the asset ID (e.g. "A1").
Both variants are mechanical / non-AI-gated and validate only that
every referenced component exists on the model. An asset's answer
carries the model's id, version and title, and entities.assets the
asset as it now stands. |
| get_scan_promptA | Get guidance prompts for scanning a codebase. Read-only; no side effects. kind selects which scan brief to return:
"security" (default) — prompts telling the agent what evidence
to look for per security control; only NOT_IMPLEMENTED controls are
included (implemented ones need no scan). Use this to drive a
gap-discovery pass, then record what is missing with
submit_findings and what is present with submit_assertions.
Pass control_id to scope the prompt to one control; empty
(default) returns prompts for all not-yet-implemented controls.
"functional" — the agent brief for implementing
functional-conformance tests. Generation specifies the functional
tests, so for each test not yet verified this returns its
implementation brief and the objectives it proves; it also reports
objectives_without_tests (regenerate or add a test) and
missing_objectives (applicable conditions with no objective
yet). Drive test implementation from it, then call
submit_assertions (functional_test_id) with TEST_EXISTS + TEST_ATTESTED
assertions so CI verifies each test; read the resulting pass/fail
state via get_functional_coverage. control_id does not
apply to this kind and is ignored.
|
| set_control_objective_calA | Set the per-CO ISO/SAE 21434 Cybersecurity Assurance Level (CAL). CAL is a 1-4 grade on each individual control objective that
expresses how much assurance the control program owes for that
specific objective. It lives on the control_objectives identity
side-table — writes do NOT create a new threat-model version, and
the value survives soft-delete + revival of the CO. Pass cal=None (or omit it) to clear the value. |
| revalidate_entity_qualityA | Re-run quality validation on a threat model's existing assets and
attackers, as if they were freshly generated. A fast first-pass check
judges every entity; only the ones it flags get a deeper review that
confirms them, sharpens their wording, or flags them for you. Use this to apply validation improvements to an already-generated model, or
to clear stale quality warnings — without regenerating the whole model
(which would destroy controls, assertions, and components). It is
non-destructive: an entity that should be removed is left in place with a
quality warning rather than deleted, so no control objective loses its asset
or attacker anchor. It creates no new model version: the re-validation is
queued and runs in the background, and the refreshed warnings appear on the
next read of the model. May consume credits for the entities that need the deeper review; a model
already in good shape costs nothing. Returns at once with
{"accepted": true, "queued": <entities queued>, "model": {...}}, where
model is the id, version and title of the model as it stands before
the re-validation lands. |
| auto_remediate_complianceA | Automatically close compliance gaps for a framework. Requires PRO tier. Three-phase loop: (1) auto-map existing controls to unmapped requirements,
(2) exclude requirements for non-applicable taxonomy primitives,
(3) suggest and apply new assets/attackers for remaining gaps. Phase (3) routes every proposal whose name matches a soft-deleted
asset/attacker through the same restore-candidate LLM gate
add_asset uses, so reanimating a previously removed entity
reinstates its stable ID and every CO tombstone + control tied to
it (rather than spawning a duplicate fresh ID). The response
distinguishes assets_added / attackers_added (genuinely new)
from assets_restored / attackers_restored (revived soft-
deletes) and lists restored_asset_ids / restored_attacker_ids.
Proposals the gate classified as similar (or that fail-closed
on an unavailable / malformed gate response) appear under
skipped with a per-entry reason — the operator decides whether
to restore manually or rephrase. Converges automatically: stops when fully covered or when no further
progress can be made. This runs automatically when a framework is selected, but can be
re-triggered manually if the model changes. |
| get_control_work_orderB | The work order for one control: the ticket to read BEFORE
implementing it. Read-only. Returns {model_id, model_version, control, objectives, max_tier, scan_brief, assertion_contract, acceptance_criteria, required_evidence, steps, reconcile_rules, delegation, open_proposals, provenance}:
where to look, what counts as proof (assertion_contract: the types
by soundness class, the evidence and universal_rule, the
sound_types the platform takes, what to submit with, when the
control counts as verified), what to do when the code disagrees with
the model, what this agent may decide alone (delegation), and
whether code or description is authoritative (provenance). Where the order names a required class for a clause,
required_evidence holds one entry per such clause: its clause,
the clause_id to put in covers, its quantifier, the
required_class, what is missing, and a suggested_submission
skeleton. The skeleton is a fill-in, not a submission: replace its
<...> placeholders; one left unfilled is refused, by this client and
again by the platform, since it would record a claim nothing backs. A
for-all clause takes [by_construction, sound_over_approximation]:
prefer typed_boundary (the type the sinks accept, its
constructors), else sink_default_deny (the sinks, the safe forms, a
reviewed allowlist). When the bound evidence is the wrong CLASS,
missing says so. acceptance_criteria is GENERATED from those
entries, so a clause that must hold at every site reads as such and no
number of tests closes it. A type's soundness is the class to branch on; behavioral is a
compatibility field for readers written before the classes. |
| reconcile_modelA | Reconcile a threat model with the code it describes. Call this after
reading the code and before (or instead of) editing the model by hand:
report what changed and what you observed, and the platform decides the
consequence of each observation. Mutating only where the platform
applies an observation (see below). Two inputs, both optional: changed_paths: the file paths that changed since the model's
recorded commit. For a code-derived model compute them with
git diff --name-only <commit_sha>..HEAD (the commit_sha from
the model's provenance). The platform maps them onto components and
reports which components changed, which paths no component claims,
and whether a refresh is recommended.
observations: what you saw in the code that the model does not
say. Each observation lands in one of four buckets by kind:
mechanism_named - the control's mechanism exists under another
name (subject_id = control id). Follow up with refine_control
using the codebase_findings returned in refine_suggested.
component_present - the code has a component the model lacks;
include a proposal ({name, repo_url?, path?, trust_boundary_ids?}).
component_absent - a modelled component has no code
(subject_id = component id).
forbidden_behavior - the code does something the model rules
out (subject_id = control id, or empty).
The platform decides the consequence. Proposals are never applied on
the agent's word, with one exception: a component change on a
code-derived model (provenance kind="code") is applied immediately
and queued for a person's review as applied_pending_review. Every
other proposal waits for decide_proposal. Forbidden behaviors
become findings. Observations the platform could not use come back in
ignored with the reason. |
| create_proposalA | Raise a proposal to change a model's scope or design. Call this when
the code or your analysis says the model should gain or lose a
component, or that an attacker position or asset should be removed by
design; do not edit the model directly for those changes. Mutating:
persists a proposal record. A proposal is a change of scope or design. Raising one is not deciding
it: a person (or an agent under a delegation rule that names the
decision) decides it with decide_proposal. Design changes are never
applied automatically. Poll list_proposals for the outcome. |
| list_proposalsA | List proposals and escalations on a model. Call this to poll the
outcome of a proposal you raised, or of a judgment you were refused.
Read-only; no side effects. Statuses: proposed and applied_pending_review are open;
accepted, rejected, reverted, superseded are closed.
Kinds include add_component, remove_component,
design_change, assumption, and decision_request: an
escalation of a judgment this agent was refused. A 403 from
update_finding, create_risk_acceptance, submit_attestation
or decide_proposal carries an
escalation_id; that escalation appears here as a
decision_request. An attestation refused does not 403: the edit of
edit_trust_boundary, edit_attacker or add_attacker lands
unattested and its answer's attestation carries the
escalation_id, bound to the content it was parked over (an edit of
that content supersedes it). Poll it here until a person resolves it;
do not retry the refused call. An open row may carry shows: what accepting it does, as the person
deciding it is shown it (a parked act on the behavioural model with each
answer beside its question, whether accepting extracts and at what
estimate; a revision's sentences removed and added; a drift finding's
sentence). Words a program wrote are inside a bounded region, as data. |
| decide_proposalA | Accept or reject a proposal. Call this only when the workspace's
delegation policy names this decision for this agent at the proposal's
tier (the delegation block of get_control_work_order says what
you may decide). Mutating: closes the proposal and applies an accepted
change. This is a judgment. The call is refused with HTTP 403 and an
escalation_id unless the delegation policy permits it; the refusal
parks the decision for a person as a decision_request. Do not retry
a refusal: report the escalation_id, poll list_proposals for the
outcome, and continue other work. Accepting an assumption proposal accepts the assumption: it is
created if new, attested until expires_at and bound into the group
that waited on it, which is then judged again. That is its own decision
(assumption_accepted); a rule delegating proposal acceptance does not
cover it. Rejecting one records the precondition as rejected for that
objective, so it is not proposed again, and the objective keeps its gap. |
| decide_drift_intentA | Decide whether a description-drift finding (the code reported to do
more, or less, than a sentence of the description) was intended or is a
defect of the code. Mutating; a judgment. intended: the description is behind the product; revise it
(create_proposal kind description_revision). defect: the
description is the design and the code is wrong; name in control_id
the control whose assertions check the behaviour as designed. The finding
closes when one of its assertions passes verification at a commit not
earlier than this decision, and while it stands an answer that would
write the defect into the model is refused (409 drift_is_defect).
A program's decision is refused with 403 and an escalation_id unless
the workspace's delegation policy names drift_intent_decided: do not
retry; report it, or raise a drift_intent proposal for a person. |
| get_design_leverageA | Rank what eliminating each attacker position or asset BY DESIGN
would remove from the matrix. Call this when deciding whether to
change the design instead of implementing controls: it shows which
single design change retires the most critical and high at-risk
objectives. Read-only; no side effects. Each row in ranked is an attacker or asset with the objectives its
removal would take out of the matrix (objectives_removed, broken
down by tier in removes), how many of those are currently at risk
(removes_at_risk / removes_at_risk_by_tier), and the controls
that would be retired. Rows are ranked by critical, then high, at-risk
objectives removed. design_move (a concrete change of design that
would eliminate the row) is filled only when include_design_moves
is true. To act on a row, raise a design_change proposal with
create_proposal; never apply a design change yourself. |
| list_decisionsA | List the decision ledger of a model: every judgment recorded on it
(finding dismissed or remediated, risk accepted, not-applicable
declared, proposal accepted / rejected / reverted, assumption accepted,
behavioural model answered / confirmed, a control's delta declared, a
finding of its cross-check dismissed, a seal, position, privileges or
surface extent attested, escalation resolved, an inventory pair decided
or an entity's tags declared), newest first, with who made it and
whether it was within the workspace's delegation policy. Read-only; no
side effects. Call this BEFORE raising a proposal or asking for a judgment, so you
do not propose what a person rejected or ask again for what was
already decided. The ledger is append-only. There is no tool that edits it; to undo an
accepted proposal, revert or re-decide, never edit the record. Rows
with outcome == "refused" are judgments a program was refused;
their escalation, if any, is in list_proposals. agent is null
for a person's decision. |
| get_behavioural_modelA | Read a model's behavioural model: what the system does, as elements,
actions and zones taken from its description. Read-only. status: none (extract_behavioural_model gives it one),
needs_answers, extracted, confirmed, stale (the
description changed since) or extracting. questions are the open
ones, answered with answer_behavioural_questions; one carrying
structured takes a structured answer, one with awaiting_fix was
answered. pending_answers: collected, not extracted. ir_hash is
what confirm_behavioural_model names. waiting_on:
extraction, acceptance, answers, generation or
nothing. extraction.job: the extraction running in the
background (null when none), its progress (pass, calls done and
expected, estimated finish); extraction.dropped_elements: what it
could not keep, with the sentence to answer. generation_ready: call
generate_from_behavioural_model.
view="status" serves no IR and no questions: use it to follow an
extraction. view="questions": that and the open questions from
offset, limit of them (default 50, at most 200), with total
and next_offset. wait_seconds (0 to 50) waits for the job's
state, pass or calls completed to change, sending progress ("call 3 of
5: actions") meanwhile; at once when no job runs. Read again after
poll_after_seconds.
|
| extract_behavioural_modelA | Give a model that has no behavioural model one. Mutating only with
confirm_estimate=True; a paid action then. The extraction is paid for by whoever requests it, so it is estimated
first and the estimate is RELAYED TO THE PERSON before it is confirmed.
Never confirm on your own. Call with confirm_estimate=False (the default): nothing is charged
or written. The answer carries the estimate (the extraction, the
existing controls' deltas and checks, the link-matching call and the
re-judgement, by part), the model_version and set_revision the
model stands at, and notices on what the extraction changes:
judgements of the groups the threats reach are asked again, and
posture can move down as well as up. Show all of it to the person. Once they agree, call with confirm_estimate=True and those two
values. The extraction is queued and the call returns at once
(queued, extraction.job): it can take an hour on a long
description. Poll get_behavioural_model until extraction.job
is null; a new model version then holds the result (its status
may be needs_answers). Read the proposals with
get_behavioural_links.
Refusals come back as data, {confirmed: false, http_status, code, message}: 409 has_behavioural_model (read it with
get_behavioural_model), 409 extraction_running (one is queued or
running: poll it), 409 review_stale (the model or its controls
changed since the estimate: confirm again with the values returned), 402
(the balance cannot cover the estimate). A 404 means the caller may not
start paid actions on the model. |
| answer_behavioural_questionsA | Answer open behavioural questions, many in one call. Mutating; creates
a model version. Answer only from what the code or the owner shows; say unknown
rather than guess, in a sentence or two. An extraction runs once every
owner question has an answer, or on extract_now=True (with no
answers, refused 409 questions_open while one is open, and 409
extraction_running while one is queued or running, or with no answer
since it started: poll it), billed to the caller (402 when short). dry_run=True records nothing (no version, no escalation) and returns
outcome: whether the answers extract (extracts, estimate),
the questions left or raised. Dry-run, answer what is raised, then
submit once.
An answer is a fact (text), or a disposition its question lists:
not_needed, not_behaviour, unknown, as_stated; text
is then a note. A question about the model's own structure carries
structured: answer with structured naming the ids offered,
{"classes": [...]} or {"public": true}, {"zones": {id: zone}}, {"zone": z}, {"actions": [...]}, {"changes": {field: [actions]}}, {"receivers": [...]}, {"elements": [id | {"id", "secret": true}]} (a zone's contents: an id not offered is a
new element there; not_needed says it holds nothing). An offer with
new_zones
takes a zone it does not list, one with new_actors a receiver it
does not list, each added (409 id_in_use when the id names something
else). It edits the model at once. Where the code does more or less than
the sentence, add contradicts: {"direction": "code_does_more" | "code_does_less", "quote"?} (409 drift_is_defect over a defect's
sentence). A program's answer is a judgment: refused with 403 and an
escalation_id unless delegated. Do not retry; poll
list_proposals. With an extraction asked for, queued is true: follow it with
get_behavioural_model(view="status", wait_seconds=50). |
| edit_behavioural_elementA | Replace, or add, one element of a model's behavioural model. Mutating;
creates a model version (none when the edit changes nothing). The element is kept as a person wrote it, so a later extraction keeps it.
It changes the model's ir_hash, which voids a confirmation: confirm
again with confirm_behavioural_model. The result must still be a
valid model (422 otherwise), and a build holding the model refuses it
(409). A program's edit is a judgment. Unless the workspace's delegation policy
admits it, it is refused with 403 and an escalation_id and parked for
a person. Do not retry: report the escalation_id and poll
list_proposals. Returns the behavioural model as it now stands, with changed. |
| remove_behavioural_elementA | Remove an element of a model's behavioural model that the system does
not have (an extraction read something the system does not hold, or put
it where it is not). Mutating; creates a model version. The removal is recorded beside the model, and every later extraction
withholds what it reads back of the element, under its id or another.
Only an element nothing else names can be removed: one an action, a
channel, a copy, a claim or a zone placement still names is refused 409
referenced, naming them; edit or remove those first. An element the
model lacks is 404 unknown_element. The removal changes the model's
ir_hash, which voids a confirmation. Writing an element under a
removed id with edit_behavioural_element withdraws the removal. A removal narrows what the analysis reads, so it is a judgment: a
program's is refused with 403 and an escalation_id and parked for a
person unless the workspace's delegation policy admits it. Do not retry:
report the escalation_id and poll list_proposals. Remove only what
the documentation or the code shows the system does not have; never to
clear a question. Returns the behavioural model as it now stands; removed lists every
standing removal. |
| confirm_behavioural_modelA | Confirm a model's behavioural model as reviewed. Mutating; creates a
model version. Confirm only the model whose ir_hash was reviewed, which is the one
get_behavioural_model returns. Refused with 409: review_stale
(the model is not that hash now), stale (the description changed since
it was extracted) or needs_answers (an owner question is open). An
edit voids a confirmation. A program's confirmation is a judgment. Unless the workspace's delegation
policy admits it, it is refused with 403 and an escalation_id and
parked for a person. Do not retry: report the escalation_id and poll
list_proposals. |
| get_behavioural_linksA | Read the links from a behavioural model's elements and zones to the
model's assets and trust boundaries. Read-only; no side effects. Returns {nodes, links, proposals, proposals_hash, questions, repair}:
each link says whether it counts toward the analysis; proposals are
matches a person is shown (an empty entity_id is a node nothing
matched); proposals_hash is what accept_behavioural_link_proposals
names; questions are the open link questions (also on
get_review_queue as behavioural_link rows). A link is declared
for one node with declare_behavioural_link. |
| declare_behavioural_linkA | Declare the asset or trust boundary a behavioural element or zone is.
Mutating; creates no model version. The link is checked on every read: one whose asset lacks the property the
element carries is kept and asked about. A link that counts changes what
the analysis reaches, so the judgements it re-keys are queued, charged to
the caller. A program's declaration is a judgment. Unless the workspace's delegation
policy admits it, it is refused with 403 and an escalation_id and
parked for a person. Do not retry: report the escalation_id and poll
list_proposals. Returns the links as get_behavioural_links does, with declared. |
| accept_behavioural_link_proposalsA | Declare every link proposal as shown, in one act. Mutating; creates no
model version. Relay the proposals to the person first: read them with
get_behavioural_links and name its proposals_hash, which is the
set reviewed. Refused with 409 review_stale when a proposal has
changed since, and with 422 no_proposals when there is none. The
judgements the links re-key are queued, charged to the caller. A program's acceptance is a judgment. Unless the workspace's delegation
policy admits it, it is refused with 403 and an escalation_id and
parked for a person. Do not retry: report the escalation_id and poll
list_proposals. Returns the links as get_behavioural_links does, with declared. |
| get_behavioural_attackersA | Read the attacker closure of a model: the attackers its behavioural
model says are missing from the inventory, and what its attackers hold.
Read-only: no model is called and nothing is written. Returns {applies, credential_classes, proposals, dismissed, dismissed_zones, reported, splits, dismissed_splits, questions}.
applies is false where the closure is not offered (a model with no
inventory, or whose behavioural model decides nothing yet);
computing while what the threats reach is still being analysed, when
nothing is proposed. Each of proposals is an attacker no attacker of
the inventory stands for: its kind (own: a class proposed on its
own; grouped: a part of the zones nobody stands at, the zones of one
capability, with an entries row per zone), credential_classes,
privileges_required, capability, objectives and zones;
add it with add_proposed_attacker or dismiss it, or some zones of a
part, with dismiss_proposed_attacker. Two parts may hold the same
classes: name one by a zone of it. Each of splits is an attacker
standing for zones of several capabilities, offered split into its
parts (each listed as a proposal is), with objectives_added,
objectives_removed and ratings: split it with
split_grouped_attacker or keep it whole with keep_attacker_whole.
dismissed, dismissed_zones and dismissed_splits carry
reason, dismissed_by and dismissed_at. reported lists
narrower holders beside an attacker that stands for them. questions
ask what an attacker holds: answer each with
answer_attacker_credentials. Relay the proposals and splits to the
person before acting on them. |
| answer_attacker_credentialsA | Say which of the behavioural model's credential classes an attacker
holds. Mutating; creates a model version. Answer from what the code or the owner shows. Empty means it holds no
credential. Its privileges follow from what it holds, by rule: None
with none, High when a class may invoke an action that writes what
decides authority, Low otherwise; edit_attacker then refuses a
privileges change (409 privileges_from_holdings). A holder of a
per-tenant class is placed at every tenant-isolation boundary,
suggested. Refused with 422 unknown_class (a class the model does not
have), 409 closure_not_offered (get_behavioural_attackers reads
applies: false), 404 no_attacker. A program's answer is a judgment. Unless the workspace's delegation
policy admits it, it is refused with 403 and an escalation_id and
parked for a person. Do not retry: report the escalation_id and poll
list_proposals. Returns {model_id, model_version, applies, credential_classes, counts, attacker}: how many proposals, dismissals and questions the closure now
holds (read them with get_behavioural_attackers) and the attacker
as it now stands. |
| add_proposed_attackerA | Add the attacker the attacker closure proposes for a credential set.
Mutating; creates a model version. Name the proposal by its credential_classes as
get_behavioural_attackers lists it (a dismissed one may be added),
and a part of the zones nobody stands at by one of its zones where
another proposal holds the same classes.
The attacker holds them, with the privileges they give and no position
but the tenant-isolation boundaries a per-tenant holder is placed at.
It is rated as add_attacker rates: one call before the write,
charged to the caller, its privileges the ones it holds gives. Where no
rating answers, or when a person accepts a parked addition, it is added
unrated (rating_source: "unrated"), and
reevaluate_threat_model_factors with unrated_only rates it.
Refused with 409 not_proposed when no attacker is proposed for
exactly that set now (read the attackers again), 409
ambiguous_proposal when several are and no zone names one (its
zones lists each), 409 closure_not_offered, 422
unknown_class. A program's addition is a judgment. Unless the workspace's delegation
policy admits it, it is refused with 403 and an escalation_id and
parked for a person. Do not retry: report the escalation_id and poll
list_proposals. Returns {model_id, model_version, applies, credential_classes, counts, added, rating_source}: how many proposals, dismissals and questions the
closure now holds, added, the new attacker's id, and whether a
rating answered (unrated when none did). |
| dismiss_proposed_attackerA | Dismiss the attacker the attacker closure proposes for a credential
set, with why, or some zones of a part of the zones nobody stands at.
Mutating; creates a model version. The dismissal is kept on the model: the proposal is no longer offered
while it stands. zones names zones of a part (its kind is
grouped) that no attacker reaches: those alone are dismissed, and the
part stands for the rest; zone names the part where two proposals
hold the same classes. Refused with 409 not_proposed (no attacker is
proposed for exactly that set now), 409 ambiguous_proposal (name a
zone), 409 dismissed_already, 409 closure_not_offered, 422
unknown_class, 422 unknown_zone (a zone the part does not offer
now), 422 zones_of_own (zones of a class proposed on its own). A program's dismissal is a judgment. Unless the workspace's delegation
policy admits it, it is refused with 403 and an escalation_id and
parked for a person. Do not retry: report the escalation_id and poll
list_proposals. Returns {model_id, model_version, applies, credential_classes, counts, dismissed_set, dismissed_zones_now?}: how many proposals, dismissals and
questions the closure now holds, and what was dismissed. |
| split_grouped_attackerA | Split an attacker that stands for zones of several capabilities into
the parts get_behavioural_attackers offers under splits.
Mutating; creates a model version. Name each part by its zones as the offer lists them, which is the
split reviewed. In one write, each part is added holding what its zones
hold, its holdings attested, stating its zones, and rated as
add_attacker rates (one call per part before the write, charged to
the caller: ratings says how many); every control mapping of the
attacker's objective of an asset moves to each part's objective of that
asset with the same privilege (they owe the same obligations); and the
attacker is removed. Relay the offer to the person first. Refused with
409 split_not_offered (read the attackers again), 409
review_stale (the parts changed: read them again), 409
closure_not_offered. A program's split is a judgment. Unless the workspace's delegation
policy admits it, it is refused with 403 and an escalation_id and
parked for a person. Do not retry: report the escalation_id and poll
list_proposals. Returns {model_id, model_version, applies, credential_classes, counts, split, added, moved}: the closure's counts, the attacker split, the
parts' ids and the objectives whose control mappings moved. |
| keep_attacker_wholeA | Keep an attacker offered for a split whole, with why. Mutating;
creates a model version. The decision is kept on the model: the split is no longer offered while
it stands, and the attacker is left as it is. Refused with 409
split_not_offered, 409 dismissed_already. A program's decision is a judgment. Unless the workspace's delegation
policy admits it, it is refused with 403 and an escalation_id and
parked for a person. Do not retry: report the escalation_id and poll
list_proposals. Returns {model_id, model_version, applies, credential_classes, counts, dismissed_split}. |
| get_inventory_judgementsA | Read what the per-entity checks found about a model's inventory.
Read-only: no model is called and nothing is queued. Returns {model_id, version, duplicates, tags}. duplicates, per
kind (assets, attackers): groups (shown as one entity: both
sides' current checks say so, their texts are identical, or a person
decided it), disputed and one_sided (one check says the same and
the other does not, or only one side answered), overlaps (part and
whole), decided (the decisions in force), status (complete
when every pair a check is owed for is covered, partial with
uncovered counting the rest as queued, failed or never asked,
not_evaluated with a reason) and legacy (a report from
before the per-entity checks). tags: the entities whose tags are
neither current nor declared (unchecked) and the declarations in
force. The same rows reach get_review_queue as
inventory_judgement. Relay a disputed pair to the person; decide_inventory_pair records
the decision and request_inventory_checks asks for the checks owed. |
| request_inventory_checksA | Queue the checks a model's inventory lacks: the tag check of every
entity whose tags are not current, and the duplicate check of every entry
on a pair no current check covers. Mutating: queues background work. Those the platform made stale are recorded as its own request, the rest
under the caller. Returns {model_id, queued, platform}: the checks
queued by kind, and the platform's share. Read the results with
get_inventory_judgements once they land. |
| decide_inventory_pairA | Decide that two entries of a model's inventory are, or are not, one
entity. Mutating; creates no model version; a judgment. The decision is bound to both entries' texts: while it stands it replaces
the checks' answers for the pair, and an edit of either entry voids it.
same keeps the pair shown until a person removes one of the two
(remove_entity); nothing is merged. Refused 422 two_elements for
two assets that are each an element of the behavioural model (two
elements are two things), 422 same_entity, 404 unknown_entity. A program's decision is refused with 403 and an escalation_id unless
the workspace's delegation policy names inventory_pair_decided.
Do not retry: report the escalation_id and poll list_proposals. Returns {pair, entity_kind, verdict, decided_at, objectives},
objectives counting those resting on the two entries. |
| declare_inventory_tagsA | Declare the taxonomy primitives an entity of a model's inventory
answers. Mutating; creates no model version; a judgment. The declaration is bound to the entity's text: it witnesses each
primitive whatever the tagger answers, and an edit of the entity voids
it. Every pair must be one the taxonomy admits for the kind (422
inadmissible_tag); an empty list declares that the entity answers
none. 404 unknown_entity. A program's declaration is refused with 403 and an escalation_id
unless the workspace's delegation policy names inventory_tag_declared.
Do not retry: report the escalation_id and poll list_proposals. Returns {entity_id, entity_kind, tags, declared_at}. |
| apply_entity_proposalsA | Accept the quality check's corrections of assets and attackers, in
one new model version. Applying is accepting: a judgment normally the owner's. A program's is
parked for a person (refused, with escalation_id: do not retry)
unless the workspace delegates entity_correction_accepted. A doubted
claim you can establish, or take conservatively, is answered with
answer_entity_question instead; a property that does not apply is
declared not applicable, since a correction never removes one. Read the correction as quality on get_entity (or the review
queue's entity_quality row): proposed holds the corrected words,
proposal_hash names them, input_hash the verdict, and drops
each claim the words before made that the correction does not, with why.
RELAY proposed AND drops TO THE PERSON. A correction no reader
marked is a rewording, else a narrowing; under a rule admitting only
rewording, a claim every reader missed is applied by the program: the
residual that policy accepts. It never moves a position, credentials or
components; the entity is rated for its new words, a person's rating kept
and the new one held pending (settle_entity_rating). One outcome per entity: applied (correction_kind);
unbound (no proposal_hash: the correction and its hash are
returned); refused; property_removed (never written);
rejected (another entity, a duplicate, or internal identifiers,
reason says); stale (read it again); none; unavailable
(try again). One entity never stops the others; a model changed since it
was read writes nothing, each such item stale beside conflict. Returns {results, version?, conflict?}. |
| check_entity_qualityA | Ask for the quality check of the assets and attackers you name (every
one when you name none) that have no current verdict: an entity whose
quality reads unchecked, or one changed since its last check.
Without confirm_estimate it only estimates; with it, the checks are
queued and run in the background. Creates no model version. Checking may be paid. Call it first without confirm_estimate, RELAY
THE ESTIMATE TO THE PERSON, and confirm only when they agree; never
confirm on your own. The answer says where each named entity stands:
current (its verdict answers it as it stands; it is not checked
again: to dispute it, answer its flag with answer_entity_question),
checking (its check is queued already), answered (a stored
verdict answers it and is served again, at no cost), platform_scope
(made stale by a change to the platform; checked at Mipiti's cost) and
customer_scope (checked at the caller's cost; estimate gives an
upper bound, the deeper review running only where the first pass flags).
A confirmation the caller's balance cannot cover is refused with 402 and
queues nothing; an id naming no asset or attacker of the model is refused
400 unknown_entity. Read each entity again with get_entity for its
verdict once it lands. To ask every entity again whatever is stored, use
revalidate_entity_quality. Returns {platform_scope, customer_scope, current, checking, answered, estimate, billing, confirmed, queued, message}. |
| answer_entity_questionA | Answer, with a fact, the flag the quality check raised about an asset
or attacker. Mutating; creates no model version. An entity's quality (on get_entity, the build proposal's flagged
entities or the review queue's entity_quality row) carries the
verdict's outcome and input_hash. Any flag may be answered:
question (answer its question), defect or invalid (answer
its warning, for instance where it reads a true claim as not
established and the documentation or the code establishes it). Answer
from the documentation or the code, stating what the system does and
where that is shown; never guess, and never weaken a true claim to clear
a flag. The answer is kept with the entity, shown to every later check of
it, and queues that entity's check again (and no other's). It decides
nothing: the verdict stands until that check runs, and the check may
still flag the entity, or propose a correction to apply with
apply_entity_proposals. Where a call's response is lost, read the
entity before answering again. Refused 409 stale when the entity, or what it is judged with, changed
since the verdict (read it again), 409 not_flagged when no question,
defect or invalid verdict stands, 404 for an entity the model does not
hold. Returns {entity_id, answer_id, answered_at, question, queued}. |
| settle_entity_ratingA | Settle the rating held pending for an asset or attacker. Mutating. A rating a person chose is never replaced by a model's: where an entity's
words changed, or a re-evaluation answered over it, the model's rating is
held pending beside the person's, the more severe of the two in force.
get_entity serves the pair as rating_pending (the rating in
force and the pending one, who chose each, the words each was made for,
and pair_hash); the review queue's rating_pending row lists every
such entity. Settling is a judgment (rating_reconfirmed): a
program's is parked for a person (403 with an escalation_id: do not
retry) unless the workspace's delegation policy names it at the entity's
tier. RELAY BOTH RATINGS TO THE PERSON and settle as they choose. choice: keep_person keeps the person's rating for the words as
they stand; take_model takes the model's; set writes another,
given in factors. A choice that changes an attacker's attack vector
or privileges saves a new model version; any other writes a rating
revision. Refused 409 stale with the current pair_hash when the
pair or the entity's words moved since it was read (read it again), 409
nothing_pending when no pair is held.
Returns {entity_id, choice, version_created}. |
| get_control_deltaB | Read the delta of a control: the edits to the behavioural model that
the control's own text commits to. Read-only; no side effects. The delta is served for the control's text as it stands. status is
absent (none), pending (not checked yet), checked, failed
or declared (a person declared it). Returns {status, edits, unexpressed, applies, expressible, declared_by, rendered, unquoted, second_reading, declaration, cross_check, ...}: rendered is each
edit in words beside its quotes, unquoted the text no quote reaches. Once a person declares the delta, the control's sufficiency is judged
against the declaration's commitments, not a reading of its text, and
cross_check says whether the text may commit to more than they
state: completed (false until the control's next sufficiency
evaluation asks it) and findings, each dismissed or open. The
control reads sufficient only once its cross-check completed and every
finding is dismissed. RELAY OPEN FINDINGS TO THE PERSON: dismissing one
is a person's act, made in the app, never a program's; the other exit
is a new draft declared again. |
| submit_control_deltaA | Submit a draft of a control's delta: edits to the behavioural model,
each quoting the control's own text. Mutating; writes no control, set
revision or model version. Each edit's quote must be found in the control's text (a quote's
source defaults to the control), and unexpressed lists what the
text commits to that no edit states. An edit that does not check is
refused (422 delta_refused) with its failures. A submission is a draft, never a declaration: it is checked against the
text by one call billed to the caller (402 when the balance is short), so
it can never make the analysis credit a control alone, and a program's
never replaces a delta a person declared (409 declared_by_person).
A person declares a draft with declare_control_delta. Returns the delta as get_control_delta does, with check_ran. |
| request_delta_second_readingA | Read controls' text into deltas a second time, by a call not shown the
drafts: a delta is declared only once its draft has been read twice. Without confirm_estimate nothing is charged or written: the result
lists the controls it would read, those it skips (no_draft, or
read already) and the estimate, which must be RELAYED TO THE PERSON
first. Never confirm on your own. With it the reading is made, billed to
the caller (402 when the balance is short), and each control's outcome
is returned (usable, unusable, failed, unavailable).
Refused with 409 generation_active while a build holds the model. |
| declare_control_deltaA | Prepare the declaration of a control's delta as served: the act that
makes it the control's specification. Declaring is a person's act, never
a program's. Without confirm (the default) this is the preview, and nothing is
written: the declaration the given spans and resolutions make, its
declaration_hash, each edit in words beside its quotes, the second
reading's differences and those still open, the text no quote
reaches (unquoted), the spans the text's notes suggest as committing
to nothing, what declaring does, and blocking (what stops it). Relay
the preview to the person: the differences, and which text commits to
nothing, are theirs to decide. With confirm and the declaration_hash previewed, a program's call
is always refused with 403 and an escalation_id, whatever the
workspace delegates: it is parked for a person, who sees what it declares
and accepts or rejects it. Do not retry: report the escalation_id and
poll list_proposals. A parked declaration is withdrawn once the delta
it names changes. |
| get_behavioural_threatsA | Read the possible threats the behavioural analysis finds for each
control objective of a model. Read-only: nothing is judged, written or
paid for. The analysis runs over the model with no controls applied. Per objective
it returns its standing, each obligation's result, the threats
(rule, obligation, elements), the default_questions whose answers
would remove them, and the grounds the analysis rests on. Returns
decides: false while the behavioural model is absent, asks an owner
question, or has not been extracted. A page at a time, in the model's order of objectives: offset and
limit (default 25, at most 200), co_id one objective (404
unknown_objective for one the model does not hold live),
standing only the objectives of that standing (possible_threat,
undecided, ...). The answer carries total (the objectives
matching), next_offset (where the next page starts, null at the end)
and truncated (the page was cut short to stay under the size a
client can take; read on from next_offset). A possible threat is not a finding yet. RELAY IT TO THE PERSON with its
default questions first: an answer that settles a default removes the
threat, and only a threat the person confirms is worth a control. Answer
with answer_behavioural_questions. |
| request_model_checkA | Request a model check of the obligations the behavioural analysis
could not settle. Mutating only with confirm_estimate=True; a paid
action then. A check is paid for by whoever requests it, so it is estimated first and
the estimate is RELAYED TO THE PERSON before it is confirmed.
Never confirm on your own. Call with confirm_estimate=False (the default): nothing runs or is
charged. The answer carries the obligations it would cover (each
with its completion prediction), the scope, the cones
(each module, its runs, and remedies / not_run where it may
not complete or fits no runner), the estimate, the
model_version and set_revision the model stands at, and
notices on what a check does. Show all of it to the person. With
nothing to check, the message says so: name obligations to
check some anyway. Once they agree, call with confirm_estimate=True and those two
values. The check is queued (confirmed: true, job_id,
status: "queued"); poll get_model_check until terminal.
Refusals come back as data, {confirmed: false, http_status, code, message}: 409 not_confirmed (confirm the behavioural model first,
after reviewing it), differences_open (answer the open questions an
earlier check raised), check_active (one is live: read it),
generation_active (a build holds the model), review_stale (the
model or its controls changed since the estimate: confirm again with
the values returned), no_runner_size (every module is larger than
any runner holds: answer the defaults it rests on, or narrow the scope
or the obligations); 503 not_priced or no_runner_pool (checks
cannot run here yet); 402 (the balance cannot cover the estimate). A 404
means model checks are not available for the model, or the caller may
not start paid actions on it. |
| get_model_checkA | Read a model's model check: the live one, else the latest. Read-only. status is queued, extracting (the independent extraction),
running, pausing, paused, differences (the independent
extraction disagreed with the confirmed model: cross_check.questions
are the questions to answer with answer_behavioural_questions, and no
model check ran), complete, incomplete, failed,
discarded, or none when the model has never had one. terminal
says whether it has stopped. Beside it: the obligations and
scope it covers, the estimate confirmed, the results of its
runs, machine_seconds and error_message. A result that did not
complete reports no violation up to the depth reached and credits
nothing.
|
| manage_model_checkA | Pause, resume or discard a model's live model check. Mutating. action="pause": a queued check pauses at once, a running one at its
next stage (pausing until then). Idempotent. What it already ran is
charged. Returns {paused: true, job_id, status}.
action="resume": a paused check resumes at the stage it stopped in.
Returns {resumed: true, job_id, status: "queued"}.
action="discard": drops a queued or paused check; a running one is
refused until it is paused. What it already ran is charged. Returns
{discarded: true, job_id, status: "discarded"}.
A refusal comes back as data, {paused|resumed|discarded: false, http_status: 409, code, message}: not_running (no check is live),
pause_in_progress (still stopping: resume once it reads paused),
not_paused, pause_first (pause a running check before
discarding it). |