Map and govern where your AI data and traffic flow — east-west / APAC lens.
Project description
borderlint
Map and govern where your AI data and traffic flow.
A static, in-CI linter, initially for HK / GBA entities: does your AI data stay within the
jurisdictions your PDPO / PIPL policy allows? borderlint statically scans your repo (Python and
TypeScript/JavaScript) for AI provider usage, resolves each flow to a jurisdiction (ccTLD codes
plus the CN-GBA / GBA tokens), and fails the build on any flow outside the allow-list for the
data class you declare. Western and Chinese providers are treated evenly. Zero runtime dependencies.
Use
python -m borderlint scan ./service --policy residency.json --classification customer-pii
- No
--policy→ inventory mode (lists flows + jurisdictions, exits 0). --format json|mermaid|sarif|sbom— machine output, a flow map, SARIF for GitHub code-scanning, or a deterministic AI data-flow SBOM (policy-independent inventory of every flow; an artifact, exits 0).diff <baseline.sbom> <current.sbom>— compare two SBOMs; exits 1 when the PR adds a new non-localflow (new egress), else 0. Diff the base-branch SBOM against the PR's to gate new AI egress.- Accept a reviewed flow with an inline
# borderlint: allow <reason>waiver (justification required; it's reported as waived, not hidden, and can't override an explicit providerdeny). - Exit code is non-zero on a violation, so it gates CI.
Policy (the eval-set)
residency.json maps each data class to the jurisdictions you accept:
{
"home_location": "hk",
"classifications": {
"customer-pii": ["hk", "CN-GBA", "sg"],
"employee-pii": ["hk", "CN-GBA"],
"non-pii": ["hk", "CN-GBA", "cn", "mo", "sg", "us", "gb"]
}
}
Deny-by-default: a flow to any code not on the list for the declared class fails — so sg is
allowed but my is not, matching a PDPO agreed-locations EULA. GBA is shorthand for hk +
CN-GBA. Declare your home_location (hk, mo, or CN-GBA) and a flagged flow is tagged with
the data-protection regimes in play (HK PDPO / Macao PDPA / CN PIPL) and the relevant cross-border
arrangement — the matching GBA Standard Contract variant, (Mainland, Hong Kong) or (Mainland,
Macao), PIPL cross-border, or GDPR SCCs — as reference links. (home_regime
pdpo/pipl is still accepted.)
Capabilities
- Languages: Python (AST) and TypeScript/JavaScript (
import/require/ dynamicimport()), plus endpoint references in config/text files and OpenAI-compatible/v1/chat/completionscalls — even to a runtime-configured host (resolved tounknown, soon_unknown: failgates it). - Providers: 80+ across the east-west boundary — OpenAI, Anthropic, Google (Gemini + Vertex
AI), Azure, Bedrock, Mistral, Cohere, Groq, Together, Perplexity, xAI, Cerebras, Fireworks,
Replicate, SambaNova, Meta Llama + Tencent, Alibaba, DeepSeek, Moonshot, Zhipu/Z.ai, Baidu,
Volcengine, MiniMax, plus AI21 (IL), Jina (DE), Voyage, GigaChat (RU), Sarvam (IN), Scaleway &
OVHcloud (FR/EU) and region-selectable clouds (IBM watsonx, Oracle OCI, Cloudflare Workers AI,
Heroku →
unknownuntil you pin a region) — with Python and JS/TS package names and the Vercel AI SDK (@ai-sdk/*). - Image / video / speech: generation (Stability AI, Black Forest Labs/Flux, Runway, Recraft)
and speech-to-text / TTS (ElevenLabs, Deepgram, AssemblyAI, Soniox, Amazon Polly) — tagged
with their
categoryand governed for residency like any other flow. - Vector stores (data sinks): Pinecone, Weaviate Cloud, Qdrant Cloud, Zilliz/Milvus — flagged
as
vector_storeand governed for residency (region is per-cluster, so defaultunknown). - Aggregators / routers: litellm, langchain, LlamaIndex, aisuite, OpenRouter, AI/ML API, Vercel
AI core & Gateway →
unknown(runtime-routed), soon_unknown: failblocks them for sensitive classes. - Jurisdictions: ccTLD/ISO codes +
CN-GBA/GBA; AWS / Azure / GCP-Vertex region resolved from the endpoint host where present (e.g.bedrock-runtime.ap-east-1…andasia-east2-aiplatform.googleapis.com→hk). - Policy: classification-keyed JSON eval-set, deny-by-default, provider allow/deny, configurable failure set, declared home regime.
- Regimes & arrangements: home location
hk/mo/CN-GBA→ PDPO / Macao PDPA / PIPL tags, linked to the matching GBA Standard Contract (GBA to/from HK or Macao), PIPL cross-border, or GDPR SCCs — reference only. - Output & CI: text / JSON / Mermaid / SARIF / SBOM, an SBOM
diffgate for new egress, inline waivers, exit codes, GitHub Action + Jenkins.
Scope
For HK / CN / GBA / MO home bases under PDPO / PIPL / GBA / PDPA. Not yet: other jurisdictions;
CycloneDX / SPDX SBOM export and optional LLM enrichment. Per-capability status — shipped vs. next
vs. later — is tracked in CAPABILITIES.md.
Internal endpoints
Map your own regional endpoints to jurisdictions; they merge with the bundled provider KB (your entries win on conflict):
{ "endpoints": { "llm-cn.acme.internal": "cn", "llm-hk.acme.internal": "hk", "llm-sg.acme.internal": "sg" } }
borderlint scan . --providers internal-endpoints.json --policy residency.json --classification customer-pii
A configuration wired to the wrong regional endpoint — e.g. the CN endpoint for HK-only customer PII — then fails the build, so you can't ship a service pointed at the wrong region.
A runnable end-to-end example is in examples/gba-resident-app/ — a
GBA-resident app (internal Shenzhen endpoint → CN-GBA, plus Mainland / Western / local fallbacks). Run
it under residency-hk.json vs residency-mo.json and the surfaced GBA Standard Contract flips
between the (Mainland, Hong Kong) and (Mainland, Macao) variant, and the regime tag between PDPO and
Macao PDPA:
borderlint scan examples/gba-resident-app \
--providers examples/gba-resident-app/internal-endpoints.json \
--policy examples/gba-resident-app/residency-hk.json --classification customer-pii
The same scan renders to a data-flow map grouped by jurisdiction — Mermaid source in
dataflow.mmd,
rendered to PNG:
CI
Same command in any pipeline. GitHub Actions (composite action):
- uses: iolairus/borderlint@v1.1.2
with: { path: ., policy: residency.json, classification: customer-pii }
Jenkins / anything else: pip install borderlint && borderlint scan . --policy residency.json --classification customer-pii — a non-zero exit fails the stage. Full examples in examples/ci/.
pre-commit — catch a bad flow before it's committed (.pre-commit-config.yaml):
- repo: https://github.com/iolairus/borderlint
rev: v1.1.2
hooks:
- id: borderlint
args: [--policy, residency.json, --classification, customer-pii]
The hook runs borderlint scan over the repo (the args are required for a real gate; without a
policy it runs inventory mode and always passes).
Keeping the KB fresh
A weekly GitHub Action (.github/workflows/kb-refresh.yml) diffs the bundled provider KB against
litellm's registry and opens an issue listing providers we don't yet cover — jurisdictions are
assigned by hand, never auto-merged. borderlint --version shows the KB's last-reviewed date.
To add or correct a provider, see CONTRIBUTING.md (KB schema + PR workflow).
Development
borderlint is built spec-first with OpenSpec: every change
is a reviewed proposal (specs + design + tasks) gated by a spec-reviewer agent before any code is
written. To bootstrap the same workflow into another repo:
scripts/opsx-init.sh [--no-jira] /path/to/your/repo
It scaffolds AGENTS.md, .claude/ (slash commands + the spec-reviewer gate), an empty openspec/, and
workflow.yaml. --no-jira trims it to the core loop — propose → review → apply → commit → ship.
License
MIT © 2026 Iolaire McKinnon. Vendor-neutral by design.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file borderlint-1.1.2.tar.gz.
File metadata
- Download URL: borderlint-1.1.2.tar.gz
- Upload date:
- Size: 229.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8d43468be43883d97f9cd3f74bfbfd82ea444130e83c26ea8bb0949939fcba23
|
|
| MD5 |
ec2abd49e15542de8c5828d63c5d28b8
|
|
| BLAKE2b-256 |
0ed229feb5ff290dbce732cae1eed27d5cf348cd0d0d3be10cc80222868d318f
|
Provenance
The following attestation bundles were made for borderlint-1.1.2.tar.gz:
Publisher:
release.yml on iolairus/borderlint
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
borderlint-1.1.2.tar.gz -
Subject digest:
8d43468be43883d97f9cd3f74bfbfd82ea444130e83c26ea8bb0949939fcba23 - Sigstore transparency entry: 2009625756
- Sigstore integration time:
-
Permalink:
iolairus/borderlint@db3cffc77e2fe5a19781e03234e31a13addee3ba -
Branch / Tag:
refs/tags/v1.1.2 - Owner: https://github.com/iolairus
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@db3cffc77e2fe5a19781e03234e31a13addee3ba -
Trigger Event:
push
-
Statement type:
File details
Details for the file borderlint-1.1.2-py3-none-any.whl.
File metadata
- Download URL: borderlint-1.1.2-py3-none-any.whl
- Upload date:
- Size: 26.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c1db77a9b67543407ab866e020f93c181a047b22e87a0fa45929a12117b38938
|
|
| MD5 |
a107c5862bd74255649e0f67572b2519
|
|
| BLAKE2b-256 |
16c5da48476ff62817f8ae8f45795a0b5f760a3a125ac3a77ebfd9e286ce43b1
|
Provenance
The following attestation bundles were made for borderlint-1.1.2-py3-none-any.whl:
Publisher:
release.yml on iolairus/borderlint
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
borderlint-1.1.2-py3-none-any.whl -
Subject digest:
c1db77a9b67543407ab866e020f93c181a047b22e87a0fa45929a12117b38938 - Sigstore transparency entry: 2009625891
- Sigstore integration time:
-
Permalink:
iolairus/borderlint@db3cffc77e2fe5a19781e03234e31a13addee3ba -
Branch / Tag:
refs/tags/v1.1.2 - Owner: https://github.com/iolairus
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@db3cffc77e2fe5a19781e03234e31a13addee3ba -
Trigger Event:
push
-
Statement type: