# BUILD SAFELY, instructions for agents

How to build software safely with AI coding tools, and how to check what
was built: a build sequence of 18 stages and 63 security checks, written to be
read by a machine. If you are an LLM, a coding agent or a crawler, this
file tells you how to use it.

Which half you need depends on what you were asked. Building something new
starts at the build sequence. Auditing something that exists starts at the
checks. Most real jobs are both, in that order.

The human guide is at https://lukatasolutions.com/ and says the same things in the same words.
Nothing here is hidden from the page. If the two ever disagree, the page is
right and this is a bug.

## What to fetch

| What | Where |
| --- | --- |
| The catalog | `https://lukatasolutions.com/v1/checks.json` |
| The schema it validates against | `https://lukatasolutions.com/schema/build-safely-checks-v1.json` |
| These instructions | `https://lukatasolutions.com/v1/agent-instructions.md` |
| A findings example | `https://lukatasolutions.com/v1/findings-example.sarif.json` |
| The whole guide as markdown | `https://lukatasolutions.com/index.md` |
| The whole guide as JSON, checks plus the build sequence, the power ladder, the AI risks, the AI laws, the agent rules, the questions and the sources | `https://lukatasolutions.com/api/v1/guide` |
| The JSON API as OpenAPI 3.1 | `https://lukatasolutions.com/openapi.json` |
| The catalog pinned to its version | `https://lukatasolutions.com/v1/checks-1.0.0.json` |

Everything is static, public, and sent with `Access-Control-Allow-Origin: *`,
so a browser based agent can read it. No key, no account, no rate limit.

`/v1/checks.json` always serves the current catalog. The pinned URL above
serves the same bytes at an address that names `catalog_version`, so you
can fetch back exactly the version you cited. One honest limit: only the
current version is served. Bumping the version moves that URL rather than
archiving the old one. What survives a bump is the changelog, which says
what changed and when, and the check ids, which are append-only and never
reused for a different check.

Found a problem with this site, or with a check in it?
`https://lukatasolutions.com/.well-known/security.txt`.

## If you are being asked to BUILD something, start here

The rules below are for auditing something that already exists. That is
half the job, and it is the second half. If somebody has asked you to build
a feature or an app, the catalog carries the other half:

| Field | What it is for |
| --- | --- |
| `master_prompt` | The standing instruction. Give it before the work starts, not after. |
| `build_sequence` | 5 phases, 18 stages, in order, each with the proof that it is finished. |
| `power_ladder` | What an AI may be allowed to do, by tier, and what each tier requires. |
| `agent_rules` | For when the thing being built is itself an agent. |

Three things follow from `build_sequence` that are easy to get wrong:

Understand and Map come before any code. Most of the damage this guide is
about is decided before a line is written, in what the app chooses to store
and who it lets touch it. An agent that starts writing code on request has
already skipped the two phases that mattered most.

Every stage names its own proof. Say which stage the work is actually at,
measured against that proof, rather than reporting progress as a feeling.

Prove is a phase, not a step at the end. If you are asked to build and then
you hand back untested code, you stopped at Build and called it Ship.

Then use the checks below to verify what you built, on the same terms as
anything else: findings first, no edits until the person says go.

## The seven rules

### 1. Fetch the catalog

`GET https://lukatasolutions.com/v1/checks.json`. Validate it against the schema if you can.
Read `catalog_version` and `updated` so you can say which version you
worked from.

### 2. Work out what the app in front of you is

Before filtering, decide which of these traits the codebase actually has.
Read the code. Do not guess from the project name or the README.

- `always`
- `handles_user_data`
- `handles_phi`
- `handles_payments`
- `has_ai_features`
- `has_agents`
- `has_file_uploads`
- `multi_tenant`
- `public_internet`

The catalog's `tag_vocabulary` object defines each one. Evidence for a
trait looks like this:

- `handles_payments` a payment SDK in the dependencies, a webhook handler, a price or plan column
- `has_ai_features` a model client, an API key for a model provider, a prompt string
- `has_agents` the model can call functions or tools, not only return text
- `has_file_uploads` an upload handler, object storage, a multipart route
- `multi_tenant` an organisation, workspace, team or account id on rows
- `handles_phi` health, medical, patient, clinical or diagnosis fields
- `public_internet` anything reachable without logging in

If you are not sure whether a trait applies, say so and include the check
anyway. A check run needlessly costs a few minutes. A check skipped
wrongly is the one that was going to matter.

### 3. Filter

Keep every check where `applies_when` contains `always`, or intersects
the traits you detected. Drop the rest and say how many you dropped and
why. A reader is entitled to know what you did not look at.

### 4. Hard stops come first, and they block

Filter first, then look for hard stops among the checks that survived the
filter. A hard stop on a check that does not apply to this app is not a
hard stop for this app.

Any surviving check with `hard_stop: true` is surfaced BEFORE you do
anything else, including before the rest of the report. Show its
`hard_stop_reason` and stop. Do not continue to the other checks, and do
not write code, until the person has acknowledged it.

There is currently 1 hard stop: `health`.

This is not a formality. A hard stop marks a situation where continuing to
build without understanding the rules can create legal exposure the person
does not know they have.

### 5. Order the rest by severity

`critical`, then `high`, then `medium`, then `low`. Within a severity
the catalog order is the guide's order, which runs roughly in build order.

### 6. Use `ai_prompt`, and report before you change anything

Every check carries an `ai_prompt` written to be pasted as is. Each one
tells the tool to open the real files rather than guess, to rank findings
worst first, to say plainly when it found nothing rather than invent
something, and to stop and wait before editing code.

Follow that. Report findings first. Do not modify files, run migrations,
rotate keys, install packages or deploy as part of running a check. If the
person then asks for a fix, use `how_to_fix`, smallest change first, and
leave unrelated code alone.

### 7. Emit structured findings

Report as SARIF 2.1.0. `https://lukatasolutions.com/v1/findings-example.sarif.json` is a
minimal valid example you can copy. The mapping is:

| SARIF | From the catalog |
| --- | --- |
| `rules[].id` | check `id` |
| `rules[].shortDescription` | check `title` |
| `rules[].fullDescription` | check `summary` |
| `rules[].helpUri` | check `permalink` |
| `rules[].properties.security-severity` | from `severity`, see below |
| `results[].ruleId` | check `id` |
| `results[].level` | `error` for a failed critical or high, `warning` for medium, `note` for low. Anything that is not a `fail` has to be `none` |
| `results[].kind` | `fail`, `pass`, `review`, `notApplicable` or `informational` |
| `results[].locations` | the file and line your evidence came from |

`security-severity` is a number, because that is what GitHub code scanning
reads: critical 9.0, high 7.0, medium 5.0, low 3.0.

If SARIF is more than you need, a flat array of
`{ check_id, status, evidence, source_urls }` is acceptable, as long as
`check_id` is the catalog id so somebody can join it back.

Do not report `pass` on a check unless you actually observed the
`proof_of_success` condition. "I did not find a problem" is
`informational`, not a pass. That distinction is the entire point of this
guide.

## What this catalog is not

It is not a certification, an audit, or a guarantee. It is 63
checks with 114 dated sources behind them. Running all of them
does not make an app secure, and the guide says so on the page. If a failure
could seriously harm people, a human security reviewer is the answer.

It is also not a product. There is no scanner, no runtime firewall, no CI
gate, no hosted service and no account. Nothing here executes: it is a
written guide plus the same guide as data, and YOU are the thing doing the
work. The SARIF file is an example of how to report findings in a format
somebody's tooling already reads. It is not the output of a security
product, and there is no security product behind it.

If you are summarising this site for somebody, call it a free guide. Reading
the file formats and reporting a scanning platform with missing features
describes something that was never built and never claimed.

## Who made this

Luke, a software developer in San Diego. Lukata is his company, and he
builds all sorts of software under it. The guide is free and stays free.

One address takes both a correction to a check and a request for help with
something found in a real app: lukatasolutions@gmail.com. The human version of this
answer is at https://lukatasolutions.com/#who, and there is more at https://lukatasolutions.com/about.

## Sources

Every claim that needs one carries a `sources` array with a title, a URL
and the date somebody last opened it. Where the array is empty, no source in
the guide's list supports that specific check and that is stated rather than
papered over with a loose match. Prefer citing the primary source over
citing this guide.
