The annotation language
GAL is a small language with twenty verbs. This page is about how they fit
together: which ones declare things, which ones make a claim about code, and
what has to be true before a finding appears or goes away. For the syntax of
each verb, the GAL reference is generated from
guardlink gal and lists all of them.
Nouns are declared once, claims reference them
Section titled “Nouns are declared once, claims reference them”Every verb is one of two kinds.
Declarations name a thing and give it an #id. @asset, @threat,
@control, and @actor live together in .guardlink/definitions.js, and they
are the vocabulary the rest of the model draws on.
// @asset App.Users (#users)// @threat SQL_Injection (#sqli) [critical] cwe:CWE-89// @control Parameterized_Queries (#prepared-stmts)Claims say something about a specific place in the code, and they reference those ids rather than repeating their names.
@source file:src/users.js line:3 symbol:findUserByEmail@exposes #users to #sqli [critical] -- "email reaches the SQL string"The split is what makes the model checkable. A claim that references #sqli
either resolves to a declared threat or it does not, and guardlink validate
fails on the second case with a dangling-reference error. Names alone could not
do that: two spellings of “SQL injection” would silently be two threats.
The life of a finding
Section titled “The life of a finding”@exposes is the verb that creates work. Everything else either closes that
work, escalates it, or records a fact beside it.
flowchart TB
D["@asset, @threat, @control<br/>declared once, each with an #id"] --> E["@exposes<br/>opens a finding"]
E --> Q{"what is claimed next?"}
Q -->|"@mitigates"| M["closed, and a control is named"]
Q -->|"@accepts"| A["closed, and a person owns the decision"]
Q -->|"@confirmed"| C["still open, now verified exploitable"]
Q -->|"nothing"| O["still open, and ci --strict fails"]
class M,A emphasis
Only two verbs close a finding, and they close it for different reasons.
@mitigates says a control handles it and names which one. @accepts says a
person decided to carry the risk. Both remove it from the open list, and the
model keeps the distinction, so “we fixed it” and “we decided not to” never
collapse into the same silence.
The @exposes stays in both cases. Deleting it would delete the reason the
control exists, and a later reader would have no way to tell a path that was
never risky from one that was made safe.
Everything else records context
Section titled “Everything else records context”The remaining verbs do not open or close anything. They attach facts that a reader, a report, or a downstream tool needs.
| What you are recording | Verbs |
|---|---|
| How data moves, and where trust changes | @flows, @boundary |
| What an asset holds and who answers for it | @handles, @owns, @audit |
| What you are taking on faith, and what checks a control | @assumes, @validates |
| Who is legitimately allowed to do what | @actor, @entitles |
| Responsibility moved elsewhere | @transfers |
| Grouping, notes, and sensitive code | @feature, @comment, @shield |
@entitles is worth singling out. It answers “is this caller already allowed to
do this by design?”, and it deliberately has no power to suppress anything: it
never closes a finding, never gates testing, and does not appear in the SARIF
export at all. It only informs the recommendation a downstream tool makes. It
also cannot be written by hand or by an agent, because an over-grant would close
a real privilege escalation as by-design, so a proposal goes through
guardlink entitle and a human accepts it.
Where the language stops
Section titled “Where the language stops”GAL describes claims about code, and it stops at the edge of the repository.
It has no verb for a deployment topology, a vendor relationship, or a process,
because none of those has a file:line to anchor to. A threat model that needs
those still needs prose somewhere, and guardlink does not pretend otherwise.
More importantly, none of these verbs is checked against what the code actually
does. @mitigates ... using #prepared-stmts is a statement that a person wrote
down. GuardLink parses it, resolves its references, and reports it. It never
reads the query to see whether the parameter is really bound.
Related
Section titled “Related”- GAL reference lists every verb, its syntax, and
its examples, generated from
guardlink gal. - Install GuardLink writes the first
@exposesand closes it with a@mitigates. - The threat model as an artifact covers what these claims become once parsed.

