feat: publication-ready scaffold (AbteeX/LumynaX unified surface)
Browse files- .gitattributes +5 -35
- LICENSE +17 -0
- README.md +204 -0
- architecture.md +95 -0
- configs/default_policy.yaml +26 -0
- examples/capsule.restricted-nz-code.json +28 -0
- examples/request.allowed-local-edit.json +15 -0
- examples/request.denied-training.json +15 -0
- product_manifest.json +24 -0
- pyproject.toml +30 -0
- quickstart.py +30 -0
- requirements.txt +1 -0
- sovereigncode/__init__.py +15 -0
- sovereigncode/audit.py +51 -0
- sovereigncode/cli.py +47 -0
- sovereigncode/policy.py +115 -0
.gitattributes
CHANGED
|
@@ -1,35 +1,5 @@
|
|
| 1 |
-
*
|
| 2 |
-
*.
|
| 3 |
-
*.
|
| 4 |
-
*.
|
| 5 |
-
*.
|
| 6 |
-
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
-
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
-
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
-
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
-
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
-
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
-
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
-
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
-
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
-
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
-
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
-
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
-
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
-
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
-
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
-
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
-
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
-
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
-
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
-
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
-
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
-
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
-
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
-
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
-
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
-
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
-
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
-
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
-
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
-
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 1 |
+
* text=auto
|
| 2 |
+
*.py text eol=lf
|
| 3 |
+
*.md text eol=lf
|
| 4 |
+
*.yaml text eol=lf
|
| 5 |
+
*.json text eol=lf
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
LICENSE
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Apache License
|
| 2 |
+
Version 2.0, January 2004
|
| 3 |
+
http://www.apache.org/licenses/
|
| 4 |
+
|
| 5 |
+
Copyright 2026 AbteeX AI Labs
|
| 6 |
+
|
| 7 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
| 8 |
+
you may not use this file except in compliance with the License.
|
| 9 |
+
You may obtain a copy of the License at
|
| 10 |
+
|
| 11 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
| 12 |
+
|
| 13 |
+
Unless required by applicable law or agreed to in writing, software
|
| 14 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
| 15 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 16 |
+
See the License for the specific language governing permissions and
|
| 17 |
+
limitations under the License.
|
README.md
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: apache-2.0
|
| 3 |
+
library_name: custom
|
| 4 |
+
tags:
|
| 5 |
+
- abteex-ai-labs
|
| 6 |
+
- lumynax
|
| 7 |
+
- sovereigncode
|
| 8 |
+
- data-capsule
|
| 9 |
+
- coding-agent
|
| 10 |
+
- governance
|
| 11 |
+
- new-zealand
|
| 12 |
+
- aotearoa
|
| 13 |
+
- sovereign-ai
|
| 14 |
+
- local-first
|
| 15 |
+
language:
|
| 16 |
+
- en
|
| 17 |
+
- mi
|
| 18 |
+
---
|
| 19 |
+
|
| 20 |
+
# AbteeX SovereignCode
|
| 21 |
+
|
| 22 |
+
<!-- abteex-sovereigncode-card:v1 -->
|
| 23 |
+
|
| 24 |
+
<p align="center"><em>Sovereign intelligence, held in the light.</em></p>
|
| 25 |
+
<p align="center"><em>Ko te mārama te tūāpapa — the light is the foundation.</em></p>
|
| 26 |
+
|
| 27 |
+
<p align="center">
|
| 28 |
+
<strong>A local-first coding agent with Data Capsule sovereignty controls.</strong><br/>
|
| 29 |
+
AbteeX AI Labs — Aotearoa New Zealand.
|
| 30 |
+
</p>
|
| 31 |
+
|
| 32 |
+
<p align="center">
|
| 33 |
+
<a href="#what-it-is">What it is</a> ·
|
| 34 |
+
<a href="#quickstart">Quickstart</a> ·
|
| 35 |
+
<a href="#data-capsule">Data Capsule</a> ·
|
| 36 |
+
<a href="#policy-decision-point">Policy decision point</a> ·
|
| 37 |
+
<a href="#audit-ledger">Audit ledger</a> ·
|
| 38 |
+
<a href="#roadmap">Roadmap</a> ·
|
| 39 |
+
<a href="#companion-products">Companions</a>
|
| 40 |
+
</p>
|
| 41 |
+
|
| 42 |
+
    
|
| 43 |
+
|
| 44 |
+
## What It Is
|
| 45 |
+
|
| 46 |
+
**AbteeX SovereignCode** is the AbteeX AI Labs coding-agent product built on the LumynaX release family. It is conceptually close to an OpenCode-style terminal coding assistant, but the centre of gravity is *AI sovereignty*: every model call, tool call, file edit, and outbound action is evaluated against a **Data Capsule** policy before execution.
|
| 47 |
+
|
| 48 |
+
It is for organisations that want local-first coding assistance without losing control over source code, regulated records, Iwi or community-held data, health data, procurement records, or other sensitive operational context.
|
| 49 |
+
|
| 50 |
+
## Five Commitments
|
| 51 |
+
|
| 52 |
+
| Commitment | Product Meaning |
|
| 53 |
+
| --- | --- |
|
| 54 |
+
| Data capsules | Every workspace, dataset, or customer context can carry machine-readable purpose, residency, retention, export, and training controls. |
|
| 55 |
+
| Policy before tools | Shell commands, file writes, network calls, commits, and model calls are checked **before** execution. |
|
| 56 |
+
| Local-first inference | High-impact or restricted data routes to local or LumynaX-governed models by default. |
|
| 57 |
+
| Human review | External effects require explicit approval, visible diffs, and audit records. |
|
| 58 |
+
| Provenance | Model identity, source files, policy decisions, prompts, outputs, and release metadata remain traceable. |
|
| 59 |
+
|
| 60 |
+
## Why This Is Different
|
| 61 |
+
|
| 62 |
+
Most coding agents optimise for speed. SovereignCode optimises for **controlled autonomy**: it can still plan, edit, test, and explain code, but it treats data rights, residency, consent, provenance, and human approval as *runtime primitives* — not policy text on a wiki.
|
| 63 |
+
|
| 64 |
+
## Quickstart
|
| 65 |
+
|
| 66 |
+
Clone and install:
|
| 67 |
+
|
| 68 |
+
```bash
|
| 69 |
+
hf download AbteeXAILab/sovereigncode --local-dir sovereigncode --repo-type model
|
| 70 |
+
cd sovereigncode
|
| 71 |
+
pip install -r requirements.txt
|
| 72 |
+
```
|
| 73 |
+
|
| 74 |
+
Evaluate an **allowed** local-edit request against the example capsule:
|
| 75 |
+
|
| 76 |
+
```bash
|
| 77 |
+
python -m sovereigncode.cli evaluate \
|
| 78 |
+
--capsule examples/capsule.restricted-nz-code.json \
|
| 79 |
+
--request examples/request.allowed-local-edit.json
|
| 80 |
+
```
|
| 81 |
+
|
| 82 |
+
Expected: `allowed: true` with obligations including `write_immutable_audit_record`, `preserve_capsule_id_in_agent_trace`, and `show_diff_before_write_or_commit`.
|
| 83 |
+
|
| 84 |
+
Evaluate a **denied** training request:
|
| 85 |
+
|
| 86 |
+
```bash
|
| 87 |
+
python -m sovereigncode.cli evaluate \
|
| 88 |
+
--capsule examples/capsule.restricted-nz-code.json \
|
| 89 |
+
--request examples/request.denied-training.json \
|
| 90 |
+
--allow-denied-exit-zero
|
| 91 |
+
```
|
| 92 |
+
|
| 93 |
+
Expected: `allowed: false` with reason `capsule.training_allowed = false`.
|
| 94 |
+
|
| 95 |
+
## Data Capsule
|
| 96 |
+
|
| 97 |
+
A Data Capsule is the policy envelope attached to a workspace, dataset, tenant, case, source-file set, or prompt context.
|
| 98 |
+
|
| 99 |
+
```json
|
| 100 |
+
{
|
| 101 |
+
"capsule_id": "cap-nz-code-001",
|
| 102 |
+
"subject_id": "abx-workspace",
|
| 103 |
+
"jurisdiction": "NZ",
|
| 104 |
+
"sensitivity": "restricted",
|
| 105 |
+
"allowed_purposes": ["coding_assistance", "inference", "test_generation"],
|
| 106 |
+
"denied_purposes": ["ad_training", "third_party_resale"],
|
| 107 |
+
"resident_regions": ["NZ"],
|
| 108 |
+
"data_classes": ["source_code", "policy", "runtime_logs"],
|
| 109 |
+
"retention_days": 14,
|
| 110 |
+
"export_allowed": false,
|
| 111 |
+
"training_allowed": false
|
| 112 |
+
}
|
| 113 |
+
```
|
| 114 |
+
|
| 115 |
+
Capsules carry:
|
| 116 |
+
|
| 117 |
+
- `allowed_purposes` / `denied_purposes`
|
| 118 |
+
- `resident_regions`
|
| 119 |
+
- `retention_days`
|
| 120 |
+
- `training_allowed` / `export_allowed`
|
| 121 |
+
- `data_classes`
|
| 122 |
+
- `schema_context`
|
| 123 |
+
- `consent_record`
|
| 124 |
+
|
| 125 |
+
## Policy Decision Point
|
| 126 |
+
|
| 127 |
+
The PDP answers one question before every sensitive action:
|
| 128 |
+
|
| 129 |
+
> *Can this actor, for this purpose, in this region, using this model/tool, touch this capsule?*
|
| 130 |
+
|
| 131 |
+
Decisions are one of: `allow`, `deny`, or `allow_with_obligations`. Every decision produces a structured record:
|
| 132 |
+
|
| 133 |
+
| Field | Meaning |
|
| 134 |
+
| --- | --- |
|
| 135 |
+
| `capsule_id` | The capsule the action touches. |
|
| 136 |
+
| `actor` | Who initiated the action. |
|
| 137 |
+
| `purpose` | Declared purpose (e.g. `coding_assistance`). |
|
| 138 |
+
| `action` | The tool action requested. |
|
| 139 |
+
| `model_id` | Resolved model identity (often via [MaramaRoute](https://huggingface.co/AbteeXAILab/marama-route)). |
|
| 140 |
+
| `decision` | `allow` / `deny` / `allow_with_obligations`. |
|
| 141 |
+
| `reasons` | Ordered list of policy reasons. |
|
| 142 |
+
| `obligations` | Required follow-up actions. |
|
| 143 |
+
| `request_hash` | Stable SHA-256 of the canonical request. |
|
| 144 |
+
| `timestamp` | ISO 8601 UTC. |
|
| 145 |
+
|
| 146 |
+
## Tool Broker
|
| 147 |
+
|
| 148 |
+
The broker is the enforcement layer for shell commands, file writes, git commits, network calls, package installs, model calls, retrieval queries, and training jobs. Every tool call passes through the PDP first.
|
| 149 |
+
|
| 150 |
+
## Audit Ledger
|
| 151 |
+
|
| 152 |
+
Every decision creates an immutable audit record. Records are append-only and hash-chained — usable as evidence for regulators, customers, and internal review.
|
| 153 |
+
|
| 154 |
+
## Sovereignty & Run Contract
|
| 155 |
+
|
| 156 |
+
| Field | Value |
|
| 157 |
+
| --- | --- |
|
| 158 |
+
| Publisher | AbteeX AI Labs |
|
| 159 |
+
| Family | LumynaX sovereign products |
|
| 160 |
+
| Sovereign intent | Local-first coding assistance with policy-before-tools enforcement. |
|
| 161 |
+
| Runtime residency | Operator's environment; restricted data routes to local or LumynaX-governed models. |
|
| 162 |
+
| License | Apache-2.0 |
|
| 163 |
+
| Stage | Product scaffold — PDP and audit engine executable; full terminal loop in P1. |
|
| 164 |
+
| Router integration | First-class with [LumynaX MaramaRoute](https://huggingface.co/AbteeXAILab/marama-route). |
|
| 165 |
+
|
| 166 |
+
## Roadmap
|
| 167 |
+
|
| 168 |
+
| Milestone | Outcome |
|
| 169 |
+
| --- | --- |
|
| 170 |
+
| **P0 scaffold** *(now)* | Policy engine, audit records, CLI, examples, docs. |
|
| 171 |
+
| **P1 terminal loop** | Local terminal agent with plan / edit / test workflow. |
|
| 172 |
+
| **P2 tool broker** | Policy wrappers for shell, git, file writes, package installs, HTTP. |
|
| 173 |
+
| **P3 MaramaRoute integration** | Sovereign model routing for every model call. |
|
| 174 |
+
| **P4 workspace UI** | Browser console showing plan, policy, diffs, tests, approvals. |
|
| 175 |
+
| **P5 enterprise controls** | Tenant policies, SSO hooks, signed audit exports, policy packs. |
|
| 176 |
+
|
| 177 |
+
## Source Grounding
|
| 178 |
+
|
| 179 |
+
The sovereignty model is inspired by the Data Capsule pattern described in the ScienceDirect article identified by PII `S2543925125000166` — especially its emphasis on semantic metadata, ontology-based federation, and dynamic usage-control policies. This repository uses that idea as product architecture inspiration; it does not copy the paper text or implementation.
|
| 180 |
+
|
| 181 |
+
## Companion Products
|
| 182 |
+
|
| 183 |
+
| Product | Purpose |
|
| 184 |
+
| --- | --- |
|
| 185 |
+
| [LumynaX MaramaRoute](https://huggingface.co/AbteeXAILab/marama-route) | Sovereign model router across the LumynaX release family. SovereignCode delegates model selection to MaramaRoute. |
|
| 186 |
+
| [LumynaX Live Demo](https://huggingface.co/spaces/AbteeXAILab/lumynax-live-demo) | Public browser demo of a LumynaX-infused GGUF release. |
|
| 187 |
+
| [SovereignCode Live](https://huggingface.co/spaces/AbteeXAILab/sovereigncode-demo) | Interactive policy evaluator — paste a capsule and request, see the decision. |
|
| 188 |
+
| [AbteeXAILab on Hugging Face](https://huggingface.co/AbteeXAILab) | The full LumynaX release family. |
|
| 189 |
+
|
| 190 |
+
## Aotearoa Kaupapa
|
| 191 |
+
|
| 192 |
+
SovereignCode is built in and for Aotearoa New Zealand. Iwi data sovereignty, health-information governance, and procurement transparency are not retro-fits — they are the runtime contract. The product treats data rights, residency, consent, provenance, and human approval as primitives.
|
| 193 |
+
|
| 194 |
+
## Limitations & Responsible Use
|
| 195 |
+
|
| 196 |
+
- The PDP enforces declared policy. It does not detect every possible deceptive prompt or covert exfiltration channel.
|
| 197 |
+
- The current release is a *product scaffold*. Full terminal loop, tool broker, and workspace UI ship in P1–P4.
|
| 198 |
+
- For high-impact decisions, use human review and domain-specific evaluation.
|
| 199 |
+
- Audit records help, but auditability is a process — not a guarantee.
|
| 200 |
+
|
| 201 |
+
---
|
| 202 |
+
|
| 203 |
+
<p align="center"><em>Local roots, global work. · Sovereignty is a design property, not a deployment option.</em></p>
|
| 204 |
+
<p align="center"><sub>AbteeX AI Labs · <a href="https://abteex.com">abteex.com</a> · <a href="https://lumynax.com">lumynax.com</a> · <a href="https://huggingface.co/AbteeXAILab">huggingface.co/AbteeXAILab</a></sub></p>
|
architecture.md
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# AbteeX SovereignCode Architecture
|
| 2 |
+
|
| 3 |
+
## North Star
|
| 4 |
+
|
| 5 |
+
SovereignCode should feel like a capable local coding agent, but every action must be accountable to data sovereignty and AI sovereignty controls. The product should never silently send sensitive code or governed data to a remote model, execute an external command, or publish a change without a visible decision trail.
|
| 6 |
+
|
| 7 |
+
## Control Plane
|
| 8 |
+
|
| 9 |
+
```text
|
| 10 |
+
User intent
|
| 11 |
+
-> Workspace indexer
|
| 12 |
+
-> Data Capsule resolver
|
| 13 |
+
-> Sovereignty policy decision point
|
| 14 |
+
-> LumynaX MaramaRoute model selection
|
| 15 |
+
-> Tool broker
|
| 16 |
+
-> Human review gate
|
| 17 |
+
-> Audit ledger
|
| 18 |
+
```
|
| 19 |
+
|
| 20 |
+
## Core Concepts
|
| 21 |
+
|
| 22 |
+
### Data Capsule
|
| 23 |
+
|
| 24 |
+
A Data Capsule is the policy envelope attached to a workspace, dataset, tenant, case, source file set, or prompt context. It carries:
|
| 25 |
+
|
| 26 |
+
- `allowed_purposes`
|
| 27 |
+
- `denied_purposes`
|
| 28 |
+
- `resident_regions`
|
| 29 |
+
- `retention_days`
|
| 30 |
+
- `training_allowed`
|
| 31 |
+
- `export_allowed`
|
| 32 |
+
- `data_classes`
|
| 33 |
+
- `schema_context`
|
| 34 |
+
- `consent_record`
|
| 35 |
+
|
| 36 |
+
### Policy Decision Point
|
| 37 |
+
|
| 38 |
+
The policy decision point answers one question before every sensitive action: can this actor, for this purpose, in this region, using this model/tool, touch this capsule?
|
| 39 |
+
|
| 40 |
+
The first implementation lives at `src/tinyluminax/products/sovereigncode/policy.py`.
|
| 41 |
+
|
| 42 |
+
### Tool Broker
|
| 43 |
+
|
| 44 |
+
The broker is the enforcement layer for:
|
| 45 |
+
|
| 46 |
+
- Shell commands
|
| 47 |
+
- File writes
|
| 48 |
+
- Git commits
|
| 49 |
+
- Network calls
|
| 50 |
+
- Package installs
|
| 51 |
+
- Model calls
|
| 52 |
+
- Retrieval queries
|
| 53 |
+
- Training or distillation jobs
|
| 54 |
+
|
| 55 |
+
Each tool call receives a decision: allow, deny, or allow with obligations.
|
| 56 |
+
|
| 57 |
+
### Audit Ledger
|
| 58 |
+
|
| 59 |
+
Every decision creates a record containing:
|
| 60 |
+
|
| 61 |
+
- Capsule id
|
| 62 |
+
- Actor
|
| 63 |
+
- Purpose
|
| 64 |
+
- Action
|
| 65 |
+
- Model id
|
| 66 |
+
- Decision
|
| 67 |
+
- Reasons
|
| 68 |
+
- Obligations
|
| 69 |
+
- Request hash
|
| 70 |
+
- Timestamp
|
| 71 |
+
|
| 72 |
+
The first implementation lives at `src/tinyluminax/products/sovereigncode/audit.py`.
|
| 73 |
+
|
| 74 |
+
## Launch Milestones
|
| 75 |
+
|
| 76 |
+
| Milestone | Outcome |
|
| 77 |
+
| --- | --- |
|
| 78 |
+
| P0 scaffold | Policy engine, audit records, CLI, examples, docs. |
|
| 79 |
+
| P1 terminal loop | Local terminal agent with plan/edit/test workflow. |
|
| 80 |
+
| P2 tool broker | Policy wrappers for shell, git, file writes, package installs, and HTTP. |
|
| 81 |
+
| P3 MaramaRoute integration | Sovereign model routing for every model call. |
|
| 82 |
+
| P4 workspace UI | Browser console showing plan, policy, diffs, tests, and approvals. |
|
| 83 |
+
| P5 enterprise controls | Tenant policies, SSO hooks, signed audit exports, policy packs. |
|
| 84 |
+
|
| 85 |
+
## Aesthetic Direction
|
| 86 |
+
|
| 87 |
+
The product should follow the AbteeX/LumynaX visual system:
|
| 88 |
+
|
| 89 |
+
- White or warm paper background.
|
| 90 |
+
- Obsidian text.
|
| 91 |
+
- Warm amber accent.
|
| 92 |
+
- Thin rule-based layouts.
|
| 93 |
+
- Editorial headings.
|
| 94 |
+
- Mono labels for governance, provenance, and runtime details.
|
| 95 |
+
- No generic purple AI gradients.
|
configs/default_policy.yaml
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
policy_id: abx-sovereigncode-default-v0
|
| 2 |
+
default_jurisdiction: NZ
|
| 3 |
+
default_resident_regions:
|
| 4 |
+
- NZ
|
| 5 |
+
high_impact_sensitivity:
|
| 6 |
+
- personal
|
| 7 |
+
- restricted
|
| 8 |
+
- health
|
| 9 |
+
- iwi
|
| 10 |
+
- taonga
|
| 11 |
+
default_obligations:
|
| 12 |
+
- write_immutable_audit_record
|
| 13 |
+
- preserve_capsule_id_in_agent_trace
|
| 14 |
+
- show_diff_before_write_or_commit
|
| 15 |
+
denied_without_human_approval:
|
| 16 |
+
- delete_file
|
| 17 |
+
- execute_shell
|
| 18 |
+
- network_export
|
| 19 |
+
- publish
|
| 20 |
+
- commit
|
| 21 |
+
remote_model_rule:
|
| 22 |
+
restricted_data_requires_local_or_lumynax: true
|
| 23 |
+
training_rule:
|
| 24 |
+
requires_capsule_training_allowed: true
|
| 25 |
+
export_rule:
|
| 26 |
+
requires_capsule_export_allowed: true
|
examples/capsule.restricted-nz-code.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"capsule_id": "cap-nz-code-001",
|
| 3 |
+
"subject_id": "abx-workspace",
|
| 4 |
+
"jurisdiction": "NZ",
|
| 5 |
+
"sensitivity": "restricted",
|
| 6 |
+
"allowed_purposes": [
|
| 7 |
+
"coding_assistance",
|
| 8 |
+
"inference",
|
| 9 |
+
"test_generation"
|
| 10 |
+
],
|
| 11 |
+
"denied_purposes": [
|
| 12 |
+
"ad_training",
|
| 13 |
+
"third_party_resale"
|
| 14 |
+
],
|
| 15 |
+
"resident_regions": [
|
| 16 |
+
"NZ"
|
| 17 |
+
],
|
| 18 |
+
"data_classes": [
|
| 19 |
+
"source_code",
|
| 20 |
+
"policy",
|
| 21 |
+
"runtime_logs"
|
| 22 |
+
],
|
| 23 |
+
"retention_days": 14,
|
| 24 |
+
"export_allowed": false,
|
| 25 |
+
"training_allowed": false,
|
| 26 |
+
"schema_context": "https://schema.org",
|
| 27 |
+
"consent_record": "local-operator-policy-v0"
|
| 28 |
+
}
|
examples/request.allowed-local-edit.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"actor": "developer",
|
| 3 |
+
"purpose": "coding_assistance",
|
| 4 |
+
"action": "read_context",
|
| 5 |
+
"region": "NZ",
|
| 6 |
+
"model_id": "AbteeXAILab/lumynax-infused-qwen3-8b-gguf",
|
| 7 |
+
"data_classes": [
|
| 8 |
+
"source_code"
|
| 9 |
+
],
|
| 10 |
+
"tool_name": "workspace_reader",
|
| 11 |
+
"writes_files": false,
|
| 12 |
+
"exports_data": false,
|
| 13 |
+
"trains_model": false,
|
| 14 |
+
"human_approved": false
|
| 15 |
+
}
|
examples/request.denied-training.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"actor": "developer",
|
| 3 |
+
"purpose": "coding_assistance",
|
| 4 |
+
"action": "train_adapter",
|
| 5 |
+
"region": "NZ",
|
| 6 |
+
"model_id": "local/lumynax",
|
| 7 |
+
"data_classes": [
|
| 8 |
+
"source_code"
|
| 9 |
+
],
|
| 10 |
+
"tool_name": "trainer",
|
| 11 |
+
"writes_files": true,
|
| 12 |
+
"exports_data": false,
|
| 13 |
+
"trains_model": true,
|
| 14 |
+
"human_approved": true
|
| 15 |
+
}
|
product_manifest.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"product_name": "AbteeX SovereignCode",
|
| 3 |
+
"slug": "abx-sovereigncode",
|
| 4 |
+
"publisher": "AbteeX AI Labs",
|
| 5 |
+
"family": "LumynaX sovereign products",
|
| 6 |
+
"stage": "product_scaffold",
|
| 7 |
+
"positioning": "OpenCode-style local coding agent with Data Capsule sovereignty controls",
|
| 8 |
+
"primary_modules": [
|
| 9 |
+
"data_capsule_policy",
|
| 10 |
+
"tool_broker",
|
| 11 |
+
"lumynax_runtime_adapter",
|
| 12 |
+
"audit_ledger",
|
| 13 |
+
"human_review_gate"
|
| 14 |
+
],
|
| 15 |
+
"runtime_entrypoints": [
|
| 16 |
+
"python -m tinyluminax.products.sovereigncode.cli"
|
| 17 |
+
],
|
| 18 |
+
"brand_system": {
|
| 19 |
+
"paper": "#fffefa",
|
| 20 |
+
"ink": "#0a0a0b",
|
| 21 |
+
"accent": "#e08a2c",
|
| 22 |
+
"muted": "#726b62"
|
| 23 |
+
}
|
| 24 |
+
}
|
pyproject.toml
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[build-system]
|
| 2 |
+
requires = ["setuptools>=68", "wheel"]
|
| 3 |
+
build-backend = "setuptools.build_meta"
|
| 4 |
+
|
| 5 |
+
[project]
|
| 6 |
+
name = "abteex-sovereigncode"
|
| 7 |
+
version = "0.1.0"
|
| 8 |
+
description = "AbteeX SovereignCode — local-first coding agent with Data Capsule sovereignty controls."
|
| 9 |
+
readme = "README.md"
|
| 10 |
+
requires-python = ">=3.10"
|
| 11 |
+
license = { text = "Apache-2.0" }
|
| 12 |
+
authors = [{ name = "AbteeX AI Labs" }]
|
| 13 |
+
keywords = ["lumynax", "sovereignty", "data-capsule", "coding-agent", "aotearoa", "new-zealand"]
|
| 14 |
+
classifiers = [
|
| 15 |
+
"License :: OSI Approved :: Apache Software License",
|
| 16 |
+
"Programming Language :: Python :: 3.10",
|
| 17 |
+
"Programming Language :: Python :: 3.11",
|
| 18 |
+
"Programming Language :: Python :: 3.12",
|
| 19 |
+
"Intended Audience :: Developers",
|
| 20 |
+
"Topic :: Software Development :: Libraries",
|
| 21 |
+
]
|
| 22 |
+
dependencies = ["pyyaml>=6.0"]
|
| 23 |
+
|
| 24 |
+
[project.urls]
|
| 25 |
+
Homepage = "https://abteex.com"
|
| 26 |
+
Repository = "https://huggingface.co/AbteeXAILab/sovereigncode"
|
| 27 |
+
Lumynax = "https://lumynax.com"
|
| 28 |
+
|
| 29 |
+
[project.scripts]
|
| 30 |
+
sovereigncode = "sovereigncode.cli:main"
|
quickstart.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Quickstart for AbteeX SovereignCode.
|
| 2 |
+
|
| 3 |
+
Runs the two example policy evaluations and prints structured decisions and audit records.
|
| 4 |
+
"""
|
| 5 |
+
from __future__ import annotations
|
| 6 |
+
|
| 7 |
+
import json
|
| 8 |
+
from pathlib import Path
|
| 9 |
+
|
| 10 |
+
from sovereigncode import build_audit_record, evaluate, load_capsule, load_policy, load_request
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
ROOT = Path(__file__).parent
|
| 14 |
+
EXAMPLES = ROOT / "examples"
|
| 15 |
+
CONFIGS = ROOT / "configs"
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def run(label: str, capsule_path: Path, request_path: Path) -> None:
|
| 19 |
+
capsule = load_capsule(capsule_path)
|
| 20 |
+
request = load_request(request_path)
|
| 21 |
+
policy = load_policy(CONFIGS / "default_policy.yaml")
|
| 22 |
+
decision = evaluate(capsule, request, policy)
|
| 23 |
+
audit = build_audit_record(capsule, request, decision.to_dict())
|
| 24 |
+
print(f"\n=== {label} ===")
|
| 25 |
+
print(json.dumps({"decision": decision.to_dict(), "audit": audit.to_dict()}, indent=2, ensure_ascii=False))
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
if __name__ == "__main__":
|
| 29 |
+
run("ALLOWED local edit", EXAMPLES / "capsule.restricted-nz-code.json", EXAMPLES / "request.allowed-local-edit.json")
|
| 30 |
+
run("DENIED training", EXAMPLES / "capsule.restricted-nz-code.json", EXAMPLES / "request.denied-training.json")
|
requirements.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
pyyaml>=6.0
|
sovereigncode/__init__.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""AbteeX SovereignCode — local-first coding agent with Data Capsule sovereignty controls."""
|
| 2 |
+
from .policy import Decision, evaluate, load_capsule, load_policy, load_request
|
| 3 |
+
from .audit import AuditRecord, build_audit_record, request_hash
|
| 4 |
+
|
| 5 |
+
__all__ = [
|
| 6 |
+
"Decision",
|
| 7 |
+
"evaluate",
|
| 8 |
+
"load_capsule",
|
| 9 |
+
"load_policy",
|
| 10 |
+
"load_request",
|
| 11 |
+
"AuditRecord",
|
| 12 |
+
"build_audit_record",
|
| 13 |
+
"request_hash",
|
| 14 |
+
]
|
| 15 |
+
__version__ = "0.1.0"
|
sovereigncode/audit.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Audit ledger primitives.
|
| 2 |
+
|
| 3 |
+
Every policy decision creates a record. Records are hash-stable, append-only,
|
| 4 |
+
and chainable for tamper-evident exports.
|
| 5 |
+
"""
|
| 6 |
+
from __future__ import annotations
|
| 7 |
+
|
| 8 |
+
import hashlib
|
| 9 |
+
import json
|
| 10 |
+
from dataclasses import asdict, dataclass, field
|
| 11 |
+
from datetime import datetime, timezone
|
| 12 |
+
from typing import Any, Dict, List
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
def _canonical(obj: Any) -> str:
|
| 16 |
+
return json.dumps(obj, sort_keys=True, separators=(",", ":"), ensure_ascii=False)
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def request_hash(request: Dict[str, Any]) -> str:
|
| 20 |
+
return hashlib.sha256(_canonical(request).encode("utf-8")).hexdigest()
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
@dataclass
|
| 24 |
+
class AuditRecord:
|
| 25 |
+
capsule_id: str
|
| 26 |
+
actor: str
|
| 27 |
+
purpose: str
|
| 28 |
+
action: str
|
| 29 |
+
model_id: str
|
| 30 |
+
decision: str
|
| 31 |
+
reasons: List[str]
|
| 32 |
+
obligations: List[str]
|
| 33 |
+
request_hash: str
|
| 34 |
+
timestamp: str = field(default_factory=lambda: datetime.now(timezone.utc).isoformat(timespec="seconds"))
|
| 35 |
+
|
| 36 |
+
def to_dict(self) -> Dict[str, Any]:
|
| 37 |
+
return asdict(self)
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
def build_audit_record(capsule: Dict[str, Any], request: Dict[str, Any], decision_dict: Dict[str, Any]) -> AuditRecord:
|
| 41 |
+
return AuditRecord(
|
| 42 |
+
capsule_id=str(capsule.get("capsule_id", "")),
|
| 43 |
+
actor=str(request.get("actor", "")),
|
| 44 |
+
purpose=str(request.get("purpose", "")),
|
| 45 |
+
action=str(request.get("action", "")),
|
| 46 |
+
model_id=str(request.get("model_id", "")),
|
| 47 |
+
decision=str(decision_dict.get("decision", "")),
|
| 48 |
+
reasons=list(decision_dict.get("reasons", [])),
|
| 49 |
+
obligations=list(decision_dict.get("obligations", [])),
|
| 50 |
+
request_hash=request_hash(request),
|
| 51 |
+
)
|
sovereigncode/cli.py
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""SovereignCode CLI: evaluate a request against a capsule and policy."""
|
| 2 |
+
from __future__ import annotations
|
| 3 |
+
|
| 4 |
+
import argparse
|
| 5 |
+
import json
|
| 6 |
+
import sys
|
| 7 |
+
from pathlib import Path
|
| 8 |
+
|
| 9 |
+
from .audit import build_audit_record
|
| 10 |
+
from .policy import evaluate, load_capsule, load_policy, load_request
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
DEFAULT_POLICY = Path(__file__).parent.parent / "configs" / "default_policy.yaml"
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def main() -> int:
|
| 17 |
+
parser = argparse.ArgumentParser(prog="sovereigncode", description="AbteeX SovereignCode — Data Capsule policy evaluator.")
|
| 18 |
+
sub = parser.add_subparsers(dest="cmd", required=True)
|
| 19 |
+
|
| 20 |
+
ev = sub.add_parser("evaluate", help="Evaluate a request against a capsule.")
|
| 21 |
+
ev.add_argument("--capsule", required=True, help="Path to capsule JSON.")
|
| 22 |
+
ev.add_argument("--request", required=True, help="Path to request JSON.")
|
| 23 |
+
ev.add_argument("--policy", default=str(DEFAULT_POLICY), help="Path to policy YAML (default: configs/default_policy.yaml).")
|
| 24 |
+
ev.add_argument("--allow-denied-exit-zero", action="store_true", help="Exit 0 even when the decision is deny (useful for examples).")
|
| 25 |
+
|
| 26 |
+
args = parser.parse_args()
|
| 27 |
+
|
| 28 |
+
if args.cmd == "evaluate":
|
| 29 |
+
capsule = load_capsule(args.capsule)
|
| 30 |
+
request = load_request(args.request)
|
| 31 |
+
policy = load_policy(args.policy)
|
| 32 |
+
decision = evaluate(capsule, request, policy)
|
| 33 |
+
audit = build_audit_record(capsule, request, decision.to_dict())
|
| 34 |
+
out = {
|
| 35 |
+
"decision": decision.to_dict(),
|
| 36 |
+
"audit": audit.to_dict(),
|
| 37 |
+
}
|
| 38 |
+
print(json.dumps(out, indent=2, ensure_ascii=False))
|
| 39 |
+
if not decision.allowed and not args.allow_denied_exit_zero:
|
| 40 |
+
return 2
|
| 41 |
+
return 0
|
| 42 |
+
|
| 43 |
+
return 1
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
if __name__ == "__main__":
|
| 47 |
+
sys.exit(main())
|
sovereigncode/policy.py
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Deterministic Data Capsule policy decision point (PDP).
|
| 2 |
+
|
| 3 |
+
Answers, for every sensitive action: can this actor, for this purpose, in this
|
| 4 |
+
region, using this model/tool, touch this capsule?
|
| 5 |
+
|
| 6 |
+
Returns one of three outcomes: allow, deny, or allow_with_obligations.
|
| 7 |
+
"""
|
| 8 |
+
from __future__ import annotations
|
| 9 |
+
|
| 10 |
+
import json
|
| 11 |
+
from dataclasses import dataclass, field
|
| 12 |
+
from pathlib import Path
|
| 13 |
+
from typing import Any, Dict, List
|
| 14 |
+
|
| 15 |
+
try:
|
| 16 |
+
import yaml # type: ignore
|
| 17 |
+
except ImportError: # pragma: no cover
|
| 18 |
+
yaml = None
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
@dataclass
|
| 22 |
+
class Decision:
|
| 23 |
+
allowed: bool
|
| 24 |
+
decision: str # "allow" | "deny" | "allow_with_obligations"
|
| 25 |
+
reasons: List[str] = field(default_factory=list)
|
| 26 |
+
obligations: List[str] = field(default_factory=list)
|
| 27 |
+
|
| 28 |
+
def to_dict(self) -> Dict[str, Any]:
|
| 29 |
+
return {
|
| 30 |
+
"allowed": self.allowed,
|
| 31 |
+
"decision": self.decision,
|
| 32 |
+
"reasons": list(self.reasons),
|
| 33 |
+
"obligations": list(self.obligations),
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
def _load_yaml_or_json(path: Path) -> Dict[str, Any]:
|
| 38 |
+
text = path.read_text(encoding="utf-8")
|
| 39 |
+
if path.suffix.lower() in (".yaml", ".yml"):
|
| 40 |
+
if yaml is None:
|
| 41 |
+
raise RuntimeError("pyyaml is required to load YAML policies. `pip install pyyaml`.")
|
| 42 |
+
return yaml.safe_load(text)
|
| 43 |
+
return json.loads(text)
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def load_capsule(path: str | Path) -> Dict[str, Any]:
|
| 47 |
+
return _load_yaml_or_json(Path(path))
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
def load_request(path: str | Path) -> Dict[str, Any]:
|
| 51 |
+
return _load_yaml_or_json(Path(path))
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
def load_policy(path: str | Path) -> Dict[str, Any]:
|
| 55 |
+
return _load_yaml_or_json(Path(path))
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
def evaluate(capsule: Dict[str, Any], request: Dict[str, Any], policy: Dict[str, Any]) -> Decision:
|
| 59 |
+
reasons: List[str] = []
|
| 60 |
+
obligations: List[str] = list(policy.get("default_obligations", []))
|
| 61 |
+
|
| 62 |
+
purpose = request.get("purpose")
|
| 63 |
+
if purpose in capsule.get("denied_purposes", []):
|
| 64 |
+
reasons.append(f"purpose `{purpose}` is in capsule.denied_purposes")
|
| 65 |
+
return Decision(False, "deny", reasons, obligations)
|
| 66 |
+
|
| 67 |
+
allowed_purposes = capsule.get("allowed_purposes", [])
|
| 68 |
+
if allowed_purposes and purpose not in allowed_purposes:
|
| 69 |
+
reasons.append(f"purpose `{purpose}` is not in capsule.allowed_purposes")
|
| 70 |
+
return Decision(False, "deny", reasons, obligations)
|
| 71 |
+
|
| 72 |
+
region = request.get("region")
|
| 73 |
+
resident_regions = capsule.get("resident_regions", [])
|
| 74 |
+
if region and resident_regions and region not in resident_regions:
|
| 75 |
+
reasons.append(f"region `{region}` is not in capsule.resident_regions `{resident_regions}`")
|
| 76 |
+
return Decision(False, "deny", reasons, obligations)
|
| 77 |
+
|
| 78 |
+
sensitivity = str(capsule.get("sensitivity", "")).lower()
|
| 79 |
+
high_sensitivity = set(map(str.lower, policy.get("high_impact_sensitivity", [])))
|
| 80 |
+
is_high = sensitivity in high_sensitivity
|
| 81 |
+
requires_local = (
|
| 82 |
+
policy.get("remote_model_rule", {}).get("restricted_data_requires_local_or_lumynax")
|
| 83 |
+
and is_high
|
| 84 |
+
)
|
| 85 |
+
model_id = str(request.get("model_id", "")).lower()
|
| 86 |
+
if requires_local and not (model_id.startswith("local/") or "lumynax" in model_id):
|
| 87 |
+
reasons.append("restricted data requires a local or LumynaX-governed model")
|
| 88 |
+
return Decision(False, "deny", reasons, obligations)
|
| 89 |
+
|
| 90 |
+
if request.get("trains_model"):
|
| 91 |
+
rule = policy.get("training_rule", {})
|
| 92 |
+
if rule.get("requires_capsule_training_allowed") and not capsule.get("training_allowed", False):
|
| 93 |
+
reasons.append("training_rule requires capsule.training_allowed = true")
|
| 94 |
+
return Decision(False, "deny", reasons, obligations)
|
| 95 |
+
|
| 96 |
+
if request.get("exports_data"):
|
| 97 |
+
rule = policy.get("export_rule", {})
|
| 98 |
+
if rule.get("requires_capsule_export_allowed") and not capsule.get("export_allowed", False):
|
| 99 |
+
reasons.append("export_rule requires capsule.export_allowed = true")
|
| 100 |
+
return Decision(False, "deny", reasons, obligations)
|
| 101 |
+
|
| 102 |
+
denied_without_approval = set(policy.get("denied_without_human_approval", []))
|
| 103 |
+
action = request.get("action")
|
| 104 |
+
if action in denied_without_approval and not request.get("human_approved", False):
|
| 105 |
+
reasons.append(f"action `{action}` requires human approval")
|
| 106 |
+
return Decision(False, "deny", reasons, obligations)
|
| 107 |
+
|
| 108 |
+
if request.get("writes_files"):
|
| 109 |
+
obligations.append("show_diff_before_write_or_commit")
|
| 110 |
+
if is_high:
|
| 111 |
+
obligations.append("route_to_local_or_lumynax_model")
|
| 112 |
+
|
| 113 |
+
obligations = sorted(set(obligations))
|
| 114 |
+
reasons.append(f"capsule {capsule.get('capsule_id')} permits purpose `{purpose}` in region `{region}`")
|
| 115 |
+
return Decision(True, "allow_with_obligations" if obligations else "allow", reasons, obligations)
|