Secure development policy
Last updated 2026-11-02
Last updated 2026-11-02.
What this policy covers
This policy says how a change to the Botseon code is made, checked and released. It describes what we do today. Where a piece is not in place yet, we say so. Each section ends with the file in our code that backs the claim. The code repository is private; we show the named file to a customer's security reviewer on request, through security@botseon.com.
Who is accountable
Nordio ApS builds and runs Botseon. The founder is the maintainer and is accountable for every change that lands, for the written rules the AI models work under, and for this policy. No change is anyone else's responsibility because a model wrote or reviewed it.
Source: SECURITY.md; .github/CODEOWNERS (the maintainers team).
Every change is a pull request
Nobody pushes to the main branch directly. Every change is proposed as a pull request. A repository rule on the main branch requires the pipeline's queue check, routes every merge through the merge queue, blocks force pushes and blocks deleting the branch. The rule lists no one who may bypass it, administrators included.
Commits carry a Developer Certificate of Origin sign-off. A check on every pull request flags a commit that lacks one. That check is advisory: it is not one of the required checks yet.
Source: the ruleset "one door to main (gate)" as recorded in
docs/operations/org-rename-checklist.md (row G-02) and docs/superpowers/specs/2026-10-24-ops-off-mac.md;
.github/workflows/dco.yml.
Required checks and the merge queue
Every pull request runs our test pipeline. A change reaches the main branch only through the merge queue, which runs the full test suite again on top of the latest main before it lands. If the required check fails, the change does not land.
Source: .github/workflows/ci.yml (the pull_request and merge_group triggers);
.github/workflows/queue-gate.yml; docs/operations/ci-runners.md.
No test, no done
A piece of work counts as done only when each of its acceptance lines is proven by a test that runs in the pipeline. Each pull request names the test that proves each line, and lists the lines it does not prove.
Source: CLAUDE.md, the rule "No test, no done".
Code review
Most changes are written and reviewed by AI models, under written rules. No person reads every change. The model that reviews a plan is a different model from the one that wrote it, so the check is independent.
This review is part of how we work. It is not yet a required check in the pipeline. Today no pipeline job runs an AI code review or a security review on every pull request, and the repository rule does not require a second person to approve a pull request. Adding a review as a required check is on our work list.
Source: docs/process/README.md (the model and review rules); .github/workflows/ holds no review
job.
Scanning the code
Every pull request runs the type checker, the linter and a check that refuses claims we may not make, such as a certification we do not hold. A pull request that changes a workflow file also runs a checker for workflow files.
A static analysis scanner (Semgrep) that runs on every pull request is being introduced. It is not in place yet.
Source: scripts/gate-manifest.json; biome.jsonc; .github/workflows/actionlint.yml;
scripts/check-claims.sh.
Secrets stay out of the code
A redaction check runs in the commit and push hooks on every machine set up from our repository. It refuses a change that carries something shaped like a password, key or token, and it prints only the file, the line number and the kind of match, never the matched text. The hooks run on the developer's machine; the pipeline does not run this check again, and GitHub's own secret scanning is not switched on for the repository today.
In the pipeline, credentials are kept as encrypted repository secrets and passed only to the step that needs them. Our list of the credentials each environment needs holds names only, never a value. When the service stores a record, it scrubs known secret shapes out of it first.
Source: .githooks/pre-commit, .githooks/pre-push, tools/checks/redact.sh,
scripts/redact-hook.test.mjs; scripts/env-credentials.mjs; packages/db/src/redact.ts.
Customer data stays in the EU
Customer data is stored in the EU only. A new organisation's model calls are limited to EU routes by default. A connector must declare where it sends data, and a destination outside the EU fails our connector checks. The outside services we rely on are listed on our sub-processors page.
Source: rule R-0123 in our work record; packages/db/migrations/0002_orgs.sql (the default
eu_only setting); packages/models/src/resolve.ts; docs/connectors/conformance.md.
Risks specific to AI bots
A bot reads e-mails, web pages and documents written by people outside your organisation, and some of that text will try to give it instructions. How a run that has read such content is restricted, when a person must approve an action before a bot takes it, and how a connector or extension is checked before it can run, are described on our Security page. Those controls are code in the service, and the tests that prove them run on every change.
Source: docs/platform/security.md; docs/reference/permission-tags.md.