From f3c92a4c782a604a0e154e02a35d2506677d0053 Mon Sep 17 00:00:00 2001 From: mdheller <21163552+mdheller@users.noreply.github.com> Date: Mon, 4 May 2026 14:08:51 -0400 Subject: [PATCH 01/12] Add portable AI carry pack spec --- docs/portable-ai-carry-packs.md | 154 ++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 docs/portable-ai-carry-packs.md diff --git a/docs/portable-ai-carry-packs.md b/docs/portable-ai-carry-packs.md new file mode 100644 index 0000000..95e608f --- /dev/null +++ b/docs/portable-ai-carry-packs.md @@ -0,0 +1,154 @@ +# Portable AI Carry Packs + +Portable AI Carry Packs are SourceOS model-carry manifests for USB/SSD-local AI kits. + +They translate a simple product promise — carry a private local AI workstation on portable storage — into governed SourceOS model-carry objects with provenance, licensing, hash pinning, model-role constraints, runtime compatibility, cache policy, and evidence expectations. + +## Boundary + +This repository owns the manifest and validation layer. It does not download weights, launch runtimes, run inference, train models, or authorize personalization. + +| Layer | Responsibility | +| --- | --- | +| `SourceOS-Linux/sourceos-model-carry` | Portable model-pack contracts, examples, provenance expectations | +| `SourceOS-Linux/sourceos-devtools` | `sourceosctl portable-ai ...` preflight, prepare, inspect, and launch-plan commands | +| `SourceOS-Linux/agent-machine` | Runtime provider activation, residency, cache, teardown, and evidence receipts | +| `SocioProphet/model-router` | Policy-aware local/hosted/personal model routing | +| `SocioProphet/model-governance-ledger` | Consent, tuning, evaluation, promotion, revocation, lineage | +| `SocioProphet/policy-fabric` | Prompt egress, host-write, network, tool, and side-effect policy | + +## Manifest families + +Portable AI Kit uses two carry-layer object families. + +### PortableAIRoot + +Describes the portable root itself: + +- root id; +- layout version; +- target filesystem expectations; +- evidence directories; +- allowed runtime directories; +- host-write policy; +- zero-trace policy; +- compatible surfaces; +- model-pack refs. + +### ModelCarryPack + +Describes one curated or BYOM model pack: + +- pack id and display name; +- model source and license; +- runtime compatibility; +- expected RAM and disk footprint; +- quantization; +- SHA-256 hash or pending hash requirement; +- task classes; +- safety posture; +- allowed network state; +- local-only default; +- explicit download/import requirement; +- evidence expectations. + +## Initial built-in profiles + +| Profile | Role | Minimum free space | Suggested models | Default policy | +| --- | --- | --- | --- | --- | +| `tiny-router` | routing, triage, rewrite | 8 GB | 1B/3B class | local-only, no tools | +| `laptop-safe` | offline fallback, basic chat, office assist | 16 GB | 3B/4B class | local-only, no prompt egress | +| `office-local` | summarization and artifact drafting | 32 GB | 3B/7B class | workroom-scoped | +| `code-local` | local coding and repo triage | 32 GB | 7B coding class | repo-scoped | +| `field-kit` | portable SSD operator kit | 64 GB | mixed small + quality fallback | evidence-first | +| `byom-gguf` | custom GGUF import | varies | user supplied | hash + license required | + +## Required manifest fields + +A `ModelCarryPack` must include: + +```json +{ + "type": "ModelCarryPack", + "apiVersion": "sourceos.model-carry/v1alpha1", + "id": "urn:srcos:model-carry-pack:laptop-safe-llama32-3b", + "displayName": "Laptop-safe Llama 3.2 3B", + "model": { + "name": "llama3.2:3b", + "family": "llama", + "parameterClass": "3b", + "quantization": "runtime-managed", + "format": "ollama-ref" + }, + "runtimeCompatibility": ["ollama-compatible", "openai-compatible-local"], + "footprint": { + "minimumFreeGb": 16, + "recommendedFreeGb": 32, + "minimumRamGb": 8, + "recommendedRamGb": 16 + }, + "provenance": { + "sourceKind": "runtime-catalog", + "sourceUrl": "ollama://llama3.2:3b", + "licenseRef": "model-license-required", + "sha256": null, + "sha256RequiredBeforeEligibility": true + }, + "taskClasses": ["summarization", "rewrite", "office-assist", "offline-fallback"], + "policy": { + "localOnlyDefault": true, + "promptEgressDefault": "deny", + "allowToolUseDefault": false, + "allowNetworkDefault": false, + "requiresExplicitImport": true, + "requiresEvidence": true + } +} +``` + +## BYOM GGUF import rules + +BYOM is supported, but not as an unmanaged URL paste. + +A BYOM pack is not route-eligible until: + +1. the file exists under the portable root or approved local source; +2. SHA-256 is computed and stored in evidence; +3. source URL or local source note is recorded; +4. license/refusal-to-attest state is explicit; +5. task classes are operator-selected; +6. prompt-egress and tool-use policy remains denied unless separately granted; +7. model-router receives an eligible model ref, not a raw path. + +## Safety posture + +SourceOS should not brand packs as "uncensored" or promise universal compliance. The product value is local control, privacy, provenance, and policy-governed autonomy, not removal of safety constraints. + +Pack labels should be operational: + +- `local-only`; +- `offline-fallback`; +- `coding`; +- `office`; +- `multilingual`; +- `low-memory`; +- `quality-fallback`; +- `byom-unverified`; +- `byom-verified`. + +## Evidence expectations + +Portable model-carry operations should emit or reference: + +- `PortablePreflightEvidence`; +- `ModelCarryPackVerificationEvidence`; +- `BYOMImportEvidence`; +- `RuntimeActivationEvidence`; +- `RouteDecisionEvidence`; +- `PortableWipeEvidence`. + +Prompt bodies must not be stored in these evidence records. Prompt hashes and governance refs are sufficient. + +## Promotion path + +After the v1alpha1 schemas stabilize here, promote canonical contracts to `SourceOS-Linux/sourceos-spec` and keep examples in this repository as installable profile references. From c5ee36dbd1e538271fa418d31834a82c05c2ab0a Mon Sep 17 00:00:00 2001 From: mdheller <21163552+mdheller@users.noreply.github.com> Date: Mon, 4 May 2026 19:35:28 -0400 Subject: [PATCH 02/12] Add ModelCarryPack schema --- contracts/model-carry-pack.schema.json | 163 +++++++++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 contracts/model-carry-pack.schema.json diff --git a/contracts/model-carry-pack.schema.json b/contracts/model-carry-pack.schema.json new file mode 100644 index 0000000..663c937 --- /dev/null +++ b/contracts/model-carry-pack.schema.json @@ -0,0 +1,163 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://schemas.srcos.ai/model-carry/model-carry-pack.schema.json", + "title": "SourceOS Model Carry Pack", + "description": "A governed portable model pack manifest for SourceOS Portable AI Kit and Local Model Door profile families.", + "type": "object", + "additionalProperties": false, + "required": [ + "schemaVersion", + "kind", + "packId", + "displayName", + "profileKey", + "model", + "runtimeCompatibility", + "footprint", + "provenance", + "taskClasses", + "labels", + "policy", + "evidence" + ], + "properties": { + "schemaVersion": { "const": "v0.1" }, + "kind": { "const": "ModelCarryPack" }, + "packId": { "type": "string", "pattern": "^urn:srcos:model-carry-pack:" }, + "displayName": { "type": "string", "minLength": 1 }, + "profileKey": { + "type": "string", + "enum": ["tiny-router", "laptop-safe", "office-local", "code-local", "field-kit", "byom-gguf"] + }, + "model": { + "type": "object", + "additionalProperties": false, + "required": ["name", "family", "parameterClass", "quantization", "format"], + "properties": { + "name": { "type": "string", "minLength": 1 }, + "family": { "type": "string", "minLength": 1 }, + "parameterClass": { "type": "string", "enum": ["sub-1b", "1b", "3b", "4b", "7b", "8b", "12b", "other"] }, + "quantization": { "type": "string", "minLength": 1 }, + "format": { "type": "string", "enum": ["ollama-ref", "gguf", "runtime-managed", "openai-compatible-local", "other"] }, + "contextWindowHint": { "type": ["integer", "null"], "minimum": 1 }, + "diskSizeHintMb": { "type": ["integer", "null"], "minimum": 1 }, + "memoryHintMb": { "type": ["integer", "null"], "minimum": 1 } + } + }, + "runtimeCompatibility": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": ["ollama-compatible", "llama.cpp", "openai-compatible-local", "vllm", "sglang", "mlx", "other"] + } + }, + "footprint": { + "type": "object", + "additionalProperties": false, + "required": ["minimumFreeGb", "recommendedFreeGb", "minimumRamGb", "recommendedRamGb"], + "properties": { + "minimumFreeGb": { "type": "integer", "minimum": 1 }, + "recommendedFreeGb": { "type": "integer", "minimum": 1 }, + "minimumRamGb": { "type": "integer", "minimum": 1 }, + "recommendedRamGb": { "type": "integer", "minimum": 1 } + } + }, + "provenance": { + "type": "object", + "additionalProperties": false, + "required": ["sourceKind", "sourceUrl", "licenseRef", "sha256RequiredBeforeEligibility"], + "properties": { + "sourceKind": { "type": "string", "enum": ["runtime-catalog", "huggingface", "local-file", "vendor", "unknown"] }, + "sourceUrl": { "type": ["string", "null"] }, + "licenseRef": { "type": "string", "minLength": 1 }, + "modelCardRef": { "type": ["string", "null"] }, + "sha256": { "type": ["string", "null"], "pattern": "^[a-fA-F0-9]{64}$" }, + "sha256RequiredBeforeEligibility": { "type": "boolean" } + } + }, + "taskClasses": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "router", + "triage", + "summarization", + "rewrite", + "office-assist", + "artifact-drafting", + "coding-assist", + "repo-triage", + "privacy-first-chat", + "offline-fallback", + "operator-assist", + "evidence-inspection", + "workroom-local", + "field-workroom", + "operator-selected" + ] + } + }, + "labels": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "local-only", + "offline-fallback", + "coding", + "office", + "multilingual", + "low-memory", + "quality-fallback", + "field-kit", + "byom-unverified", + "byom-verified", + "runtime-managed" + ] + } + }, + "policy": { + "type": "object", + "additionalProperties": false, + "required": [ + "localOnlyDefault", + "promptEgressDefault", + "allowToolUseDefault", + "allowNetworkDefault", + "requiresExplicitImport", + "requiresEvidence" + ], + "properties": { + "localOnlyDefault": { "type": "boolean" }, + "promptEgressDefault": { "type": "string", "enum": ["deny", "policy-gated"] }, + "allowToolUseDefault": { "type": "boolean" }, + "allowNetworkDefault": { "type": "boolean" }, + "requiresExplicitImport": { "type": "boolean" }, + "requiresEvidence": { "type": "boolean" }, + "eligibleForRoutingBeforeHash": { "type": "boolean", "default": false }, + "maxPromptChars": { "type": ["integer", "null"], "minimum": 1 } + } + }, + "evidence": { + "type": "object", + "additionalProperties": false, + "required": [ + "emitPackVerification", + "emitRuntimeHealth", + "emitRouteDecision", + "emitPromptHashOnly" + ], + "properties": { + "emitPackVerification": { "type": "boolean" }, + "emitRuntimeHealth": { "type": "boolean" }, + "emitRouteDecision": { "type": "boolean" }, + "emitPromptHashOnly": { "type": "boolean" } + } + }, + "notes": { "type": "string" } + } +} From 4eca0af9df31a3b65aa5e148a9da48e6c2f5e308 Mon Sep 17 00:00:00 2001 From: mdheller <21163552+mdheller@users.noreply.github.com> Date: Mon, 4 May 2026 19:36:32 -0400 Subject: [PATCH 03/12] Add PortableAIRoot schema --- contracts/portable-ai-root.schema.json | 93 ++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 contracts/portable-ai-root.schema.json diff --git a/contracts/portable-ai-root.schema.json b/contracts/portable-ai-root.schema.json new file mode 100644 index 0000000..5baaafd --- /dev/null +++ b/contracts/portable-ai-root.schema.json @@ -0,0 +1,93 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://schemas.srcos.ai/model-carry/portable-ai-root.schema.json", + "title": "SourceOS Portable AI Root", + "description": "A portable USB/SSD root manifest for a governed SourceOS local AI kit.", + "type": "object", + "additionalProperties": false, + "required": [ + "schemaVersion", + "kind", + "rootId", + "displayName", + "layoutVersion", + "profileKey", + "directories", + "surfaces", + "modelPackRefs", + "policy", + "evidence" + ], + "properties": { + "schemaVersion": { "const": "v0.1" }, + "kind": { "const": "PortableAIRoot" }, + "rootId": { "type": "string", "pattern": "^urn:srcos:portable-ai-root:" }, + "displayName": { "type": "string", "minLength": 1 }, + "layoutVersion": { "type": "string", "pattern": "^sourceos.portable-ai/" }, + "profileKey": { + "type": "string", + "enum": ["tiny-router", "laptop-safe", "office-local", "code-local", "field-kit", "byom-gguf"] + }, + "directories": { + "type": "object", + "additionalProperties": false, + "required": ["manifests", "runtimes", "models", "cache", "state", "surfaces", "evidence", "tmp"], + "properties": { + "manifests": { "type": "string" }, + "runtimes": { "type": "string" }, + "models": { "type": "string" }, + "cache": { "type": "string" }, + "state": { "type": "string" }, + "surfaces": { "type": "string" }, + "evidence": { "type": "string" }, + "tmp": { "type": "string" } + } + }, + "surfaces": { + "type": "array", + "minItems": 1, + "items": { "type": "string", "enum": ["turtleterm", "agent-term", "bearbrowser", "local-web", "anythingllm-adapter"] } + }, + "modelPackRefs": { + "type": "array", + "items": { "type": "string", "pattern": "^urn:srcos:model-carry-pack:" } + }, + "policy": { + "type": "object", + "additionalProperties": false, + "required": [ + "promptEgressDefault", + "hostWritesDefault", + "toolUseDefault", + "networkDefault", + "runtimeActivation", + "modelDownloads", + "bindAddressDefault", + "zeroTraceSupported" + ], + "properties": { + "promptEgressDefault": { "type": "string", "enum": ["deny", "policy-gated"] }, + "hostWritesDefault": { "type": "string", "enum": ["deny", "workroom-scoped", "repo-scoped", "evidence-scoped"] }, + "toolUseDefault": { "type": "string", "enum": ["deny", "policy-gated"] }, + "networkDefault": { "type": "string", "enum": ["deny", "loopback-only", "policy-gated"] }, + "runtimeActivation": { "type": "string", "enum": ["agent-machine-gated"] }, + "modelDownloads": { "type": "string", "enum": ["explicit-only"] }, + "bindAddressDefault": { "type": "string", "enum": ["127.0.0.1"] }, + "zeroTraceSupported": { "type": "boolean" } + } + }, + "evidence": { + "type": "object", + "additionalProperties": false, + "required": ["preflightDir", "materializationDir", "activationDir", "wipeDir", "promptBodiesStored"], + "properties": { + "preflightDir": { "type": "string" }, + "materializationDir": { "type": "string" }, + "activationDir": { "type": "string" }, + "wipeDir": { "type": "string" }, + "promptBodiesStored": { "type": "boolean" } + } + }, + "notes": { "type": "string" } + } +} From 3f20f6611c45cfff4c52a9914e311a7cb524c5f1 Mon Sep 17 00:00:00 2001 From: mdheller <21163552+mdheller@users.noreply.github.com> Date: Mon, 4 May 2026 19:39:37 -0400 Subject: [PATCH 04/12] Add tiny-router ModelCarryPack example --- examples/model-carry-pack.tiny-router.json | 51 ++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 examples/model-carry-pack.tiny-router.json diff --git a/examples/model-carry-pack.tiny-router.json b/examples/model-carry-pack.tiny-router.json new file mode 100644 index 0000000..8cbe52b --- /dev/null +++ b/examples/model-carry-pack.tiny-router.json @@ -0,0 +1,51 @@ +{ + "schemaVersion": "v0.1", + "kind": "ModelCarryPack", + "packId": "urn:srcos:model-carry-pack:tiny-router-small-local", + "displayName": "Tiny Router Small Local Pack", + "profileKey": "tiny-router", + "model": { + "name": "small-local-model", + "family": "local-small", + "parameterClass": "1b", + "quantization": "runtime-managed", + "format": "ollama-ref", + "contextWindowHint": 8192, + "diskSizeHintMb": 1400, + "memoryHintMb": 4096 + }, + "runtimeCompatibility": ["ollama-compatible", "openai-compatible-local"], + "footprint": { + "minimumFreeGb": 8, + "recommendedFreeGb": 16, + "minimumRamGb": 8, + "recommendedRamGb": 16 + }, + "provenance": { + "sourceKind": "runtime-catalog", + "sourceUrl": null, + "licenseRef": "model-license-required", + "modelCardRef": null, + "sha256": null, + "sha256RequiredBeforeEligibility": false + }, + "taskClasses": ["router", "triage", "rewrite", "summarization"], + "labels": ["local-only", "low-memory", "runtime-managed"], + "policy": { + "localOnlyDefault": true, + "promptEgressDefault": "deny", + "allowToolUseDefault": false, + "allowNetworkDefault": false, + "requiresExplicitImport": true, + "requiresEvidence": true, + "eligibleForRoutingBeforeHash": true, + "maxPromptChars": 24000 + }, + "evidence": { + "emitPackVerification": true, + "emitRuntimeHealth": true, + "emitRouteDecision": true, + "emitPromptHashOnly": true + }, + "notes": "Tiny local profile. Runtime-managed model references require explicit operator action." +} From 412a95294467398f33ccb12186a9887cbcb14605 Mon Sep 17 00:00:00 2001 From: mdheller <21163552+mdheller@users.noreply.github.com> Date: Mon, 4 May 2026 20:03:42 -0400 Subject: [PATCH 05/12] Add laptop-safe ModelCarryPack example --- examples/model-carry-pack.laptop-safe.json | 51 ++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 examples/model-carry-pack.laptop-safe.json diff --git a/examples/model-carry-pack.laptop-safe.json b/examples/model-carry-pack.laptop-safe.json new file mode 100644 index 0000000..1aae070 --- /dev/null +++ b/examples/model-carry-pack.laptop-safe.json @@ -0,0 +1,51 @@ +{ + "schemaVersion": "v0.1", + "kind": "ModelCarryPack", + "packId": "urn:srcos:model-carry-pack:laptop-safe-small-local", + "displayName": "Laptop-safe Small Local Pack", + "profileKey": "laptop-safe", + "model": { + "name": "small-local-chat-model", + "family": "local-small", + "parameterClass": "3b", + "quantization": "runtime-managed", + "format": "runtime-managed", + "contextWindowHint": 8192, + "diskSizeHintMb": 3200, + "memoryHintMb": 8192 + }, + "runtimeCompatibility": ["ollama-compatible", "llama.cpp", "openai-compatible-local"], + "footprint": { + "minimumFreeGb": 16, + "recommendedFreeGb": 32, + "minimumRamGb": 8, + "recommendedRamGb": 16 + }, + "provenance": { + "sourceKind": "runtime-catalog", + "sourceUrl": null, + "licenseRef": "model-license-required", + "modelCardRef": null, + "sha256": null, + "sha256RequiredBeforeEligibility": false + }, + "taskClasses": ["offline-fallback", "office-assist", "privacy-first-chat", "rewrite"], + "labels": ["local-only", "offline-fallback", "runtime-managed"], + "policy": { + "localOnlyDefault": true, + "promptEgressDefault": "deny", + "allowToolUseDefault": false, + "allowNetworkDefault": false, + "requiresExplicitImport": true, + "requiresEvidence": true, + "eligibleForRoutingBeforeHash": true, + "maxPromptChars": 32000 + }, + "evidence": { + "emitPackVerification": true, + "emitRuntimeHealth": true, + "emitRouteDecision": true, + "emitPromptHashOnly": true + }, + "notes": "Laptop-safe profile for general local fallback and private drafting." +} From cad7ce86d72ad8e3bccfa05bb6de4e23156f6353 Mon Sep 17 00:00:00 2001 From: mdheller <21163552+mdheller@users.noreply.github.com> Date: Mon, 4 May 2026 20:04:24 -0400 Subject: [PATCH 06/12] Add office-local ModelCarryPack example --- examples/model-carry-pack.office-local.json | 51 +++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 examples/model-carry-pack.office-local.json diff --git a/examples/model-carry-pack.office-local.json b/examples/model-carry-pack.office-local.json new file mode 100644 index 0000000..3f6ea94 --- /dev/null +++ b/examples/model-carry-pack.office-local.json @@ -0,0 +1,51 @@ +{ + "schemaVersion": "v0.1", + "kind": "ModelCarryPack", + "packId": "urn:srcos:model-carry-pack:office-local-small-local", + "displayName": "Office-local Small Local Pack", + "profileKey": "office-local", + "model": { + "name": "office-local-model", + "family": "local-office", + "parameterClass": "7b", + "quantization": "runtime-managed", + "format": "runtime-managed", + "contextWindowHint": 16384, + "diskSizeHintMb": 5200, + "memoryHintMb": 12288 + }, + "runtimeCompatibility": ["ollama-compatible", "llama.cpp", "openai-compatible-local"], + "footprint": { + "minimumFreeGb": 32, + "recommendedFreeGb": 64, + "minimumRamGb": 16, + "recommendedRamGb": 24 + }, + "provenance": { + "sourceKind": "runtime-catalog", + "sourceUrl": null, + "licenseRef": "model-license-required", + "modelCardRef": null, + "sha256": null, + "sha256RequiredBeforeEligibility": false + }, + "taskClasses": ["office-assist", "summarization", "artifact-drafting", "workroom-local"], + "labels": ["local-only", "office", "runtime-managed"], + "policy": { + "localOnlyDefault": true, + "promptEgressDefault": "deny", + "allowToolUseDefault": false, + "allowNetworkDefault": false, + "requiresExplicitImport": true, + "requiresEvidence": true, + "eligibleForRoutingBeforeHash": true, + "maxPromptChars": 48000 + }, + "evidence": { + "emitPackVerification": true, + "emitRuntimeHealth": true, + "emitRouteDecision": true, + "emitPromptHashOnly": true + }, + "notes": "Office Plane local drafting and summarization profile." +} From 6df1ffc0191832c9cfdcf44df5acd16dc2ada61a Mon Sep 17 00:00:00 2001 From: mdheller <21163552+mdheller@users.noreply.github.com> Date: Mon, 4 May 2026 20:04:54 -0400 Subject: [PATCH 07/12] Add code-local ModelCarryPack example --- examples/model-carry-pack.code-local.json | 51 +++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 examples/model-carry-pack.code-local.json diff --git a/examples/model-carry-pack.code-local.json b/examples/model-carry-pack.code-local.json new file mode 100644 index 0000000..5946521 --- /dev/null +++ b/examples/model-carry-pack.code-local.json @@ -0,0 +1,51 @@ +{ + "schemaVersion": "v0.1", + "kind": "ModelCarryPack", + "packId": "urn:srcos:model-carry-pack:code-local-small-local", + "displayName": "Code-local Small Local Pack", + "profileKey": "code-local", + "model": { + "name": "code-local-model", + "family": "local-code", + "parameterClass": "7b", + "quantization": "runtime-managed", + "format": "runtime-managed", + "contextWindowHint": 16384, + "diskSizeHintMb": 5200, + "memoryHintMb": 12288 + }, + "runtimeCompatibility": ["ollama-compatible", "llama.cpp", "openai-compatible-local"], + "footprint": { + "minimumFreeGb": 32, + "recommendedFreeGb": 64, + "minimumRamGb": 16, + "recommendedRamGb": 24 + }, + "provenance": { + "sourceKind": "runtime-catalog", + "sourceUrl": null, + "licenseRef": "model-license-required", + "modelCardRef": null, + "sha256": null, + "sha256RequiredBeforeEligibility": false + }, + "taskClasses": ["coding-assist", "repo-triage", "rewrite", "summarization"], + "labels": ["local-only", "coding", "runtime-managed"], + "policy": { + "localOnlyDefault": true, + "promptEgressDefault": "deny", + "allowToolUseDefault": false, + "allowNetworkDefault": false, + "requiresExplicitImport": true, + "requiresEvidence": true, + "eligibleForRoutingBeforeHash": true, + "maxPromptChars": 48000 + }, + "evidence": { + "emitPackVerification": true, + "emitRuntimeHealth": true, + "emitRouteDecision": true, + "emitPromptHashOnly": true + }, + "notes": "Local code and repository triage profile." +} From ee0dc1622b5ae8751a2db34e332c1d35fb3b47fa Mon Sep 17 00:00:00 2001 From: mdheller <21163552+mdheller@users.noreply.github.com> Date: Mon, 4 May 2026 20:40:23 -0400 Subject: [PATCH 08/12] Add field-kit ModelCarryPack example --- examples/model-carry-pack.field-kit.json | 51 ++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 examples/model-carry-pack.field-kit.json diff --git a/examples/model-carry-pack.field-kit.json b/examples/model-carry-pack.field-kit.json new file mode 100644 index 0000000..2b4e788 --- /dev/null +++ b/examples/model-carry-pack.field-kit.json @@ -0,0 +1,51 @@ +{ + "schemaVersion": "v0.1", + "kind": "ModelCarryPack", + "packId": "urn:srcos:model-carry-pack:field-kit-local", + "displayName": "Field Kit Local Pack", + "profileKey": "field-kit", + "model": { + "name": "field-kit-local-model", + "family": "local-field", + "parameterClass": "8b", + "quantization": "runtime-managed", + "format": "runtime-managed", + "contextWindowHint": 32768, + "diskSizeHintMb": 7200, + "memoryHintMb": 16384 + }, + "runtimeCompatibility": ["ollama-compatible", "llama.cpp", "openai-compatible-local"], + "footprint": { + "minimumFreeGb": 64, + "recommendedFreeGb": 128, + "minimumRamGb": 16, + "recommendedRamGb": 32 + }, + "provenance": { + "sourceKind": "runtime-catalog", + "sourceUrl": null, + "licenseRef": "model-license-required", + "modelCardRef": null, + "sha256": null, + "sha256RequiredBeforeEligibility": false + }, + "taskClasses": ["offline-fallback", "operator-assist", "evidence-inspection", "field-workroom"], + "labels": ["local-only", "offline-fallback", "field-kit", "runtime-managed"], + "policy": { + "localOnlyDefault": true, + "promptEgressDefault": "deny", + "allowToolUseDefault": false, + "allowNetworkDefault": false, + "requiresExplicitImport": true, + "requiresEvidence": true, + "eligibleForRoutingBeforeHash": true, + "maxPromptChars": 64000 + }, + "evidence": { + "emitPackVerification": true, + "emitRuntimeHealth": true, + "emitRouteDecision": true, + "emitPromptHashOnly": true + }, + "notes": "Portable SSD profile for field and operator use where evidence posture matters more than minimal footprint." +} From 45f8822e1ef5652ee09cd50cf110d382d9f714a5 Mon Sep 17 00:00:00 2001 From: mdheller <21163552+mdheller@users.noreply.github.com> Date: Mon, 4 May 2026 21:10:36 -0400 Subject: [PATCH 09/12] Add BYOM GGUF ModelCarryPack example --- examples/model-carry-pack.byom-gguf.json | 51 ++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 examples/model-carry-pack.byom-gguf.json diff --git a/examples/model-carry-pack.byom-gguf.json b/examples/model-carry-pack.byom-gguf.json new file mode 100644 index 0000000..15f4b4d --- /dev/null +++ b/examples/model-carry-pack.byom-gguf.json @@ -0,0 +1,51 @@ +{ + "schemaVersion": "v0.1", + "kind": "ModelCarryPack", + "packId": "urn:srcos:model-carry-pack:byom-gguf-unverified", + "displayName": "BYOM GGUF Unverified Pack", + "profileKey": "byom-gguf", + "model": { + "name": "operator-supplied-gguf", + "family": "operator-supplied", + "parameterClass": "other", + "quantization": "operator-supplied", + "format": "gguf", + "contextWindowHint": null, + "diskSizeHintMb": null, + "memoryHintMb": null + }, + "runtimeCompatibility": ["llama.cpp", "ollama-compatible", "openai-compatible-local"], + "footprint": { + "minimumFreeGb": 8, + "recommendedFreeGb": 64, + "minimumRamGb": 8, + "recommendedRamGb": 32 + }, + "provenance": { + "sourceKind": "local-file", + "sourceUrl": null, + "licenseRef": "operator-attestation-required", + "modelCardRef": null, + "sha256": null, + "sha256RequiredBeforeEligibility": true + }, + "taskClasses": ["operator-selected"], + "labels": ["local-only", "byom-unverified"], + "policy": { + "localOnlyDefault": true, + "promptEgressDefault": "deny", + "allowToolUseDefault": false, + "allowNetworkDefault": false, + "requiresExplicitImport": true, + "requiresEvidence": true, + "eligibleForRoutingBeforeHash": false, + "maxPromptChars": null + }, + "evidence": { + "emitPackVerification": true, + "emitRuntimeHealth": true, + "emitRouteDecision": true, + "emitPromptHashOnly": true + }, + "notes": "BYOM placeholder. It is not eligible for governed routing until hash, provenance, and license posture are recorded." +} From a8d71c1aff03ac43e881dc0d1f8604927cab3053 Mon Sep 17 00:00:00 2001 From: mdheller <21163552+mdheller@users.noreply.github.com> Date: Mon, 4 May 2026 21:23:55 -0400 Subject: [PATCH 10/12] Add PortableAIRoot example --- examples/portable-ai-root.laptop-safe.json | 40 ++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 examples/portable-ai-root.laptop-safe.json diff --git a/examples/portable-ai-root.laptop-safe.json b/examples/portable-ai-root.laptop-safe.json new file mode 100644 index 0000000..808abd5 --- /dev/null +++ b/examples/portable-ai-root.laptop-safe.json @@ -0,0 +1,40 @@ +{ + "schemaVersion": "v0.1", + "kind": "PortableAIRoot", + "rootId": "urn:srcos:portable-ai-root:laptop-safe-demo", + "displayName": "SourceOS Portable AI Laptop-safe Root", + "layoutVersion": "sourceos.portable-ai/v1alpha1", + "profileKey": "laptop-safe", + "directories": { + "manifests": "manifests", + "runtimes": "runtimes", + "models": "models", + "cache": "cache", + "state": "state", + "surfaces": "surfaces", + "evidence": "evidence", + "tmp": "tmp" + }, + "surfaces": ["turtleterm", "agent-term", "bearbrowser", "local-web"], + "modelPackRefs": [ + "urn:srcos:model-carry-pack:laptop-safe-small-local" + ], + "policy": { + "promptEgressDefault": "deny", + "hostWritesDefault": "deny", + "toolUseDefault": "deny", + "networkDefault": "loopback-only", + "runtimeActivation": "agent-machine-gated", + "modelDownloads": "explicit-only", + "bindAddressDefault": "127.0.0.1", + "zeroTraceSupported": true + }, + "evidence": { + "preflightDir": "evidence/preflight", + "materializationDir": "evidence/materialization", + "activationDir": "evidence/activation", + "wipeDir": "evidence/wipe", + "promptBodiesStored": false + }, + "notes": "Example portable root manifest for SourceOS Portable AI Kit." +} From 3b497f566badd250e9fec08bb5f7c3e152c73cfe Mon Sep 17 00:00:00 2001 From: mdheller <21163552+mdheller@users.noreply.github.com> Date: Mon, 4 May 2026 21:40:32 -0400 Subject: [PATCH 11/12] Add Portable AI pack validator --- tools/validate_portable_ai_packs.py | 142 ++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 tools/validate_portable_ai_packs.py diff --git a/tools/validate_portable_ai_packs.py b/tools/validate_portable_ai_packs.py new file mode 100644 index 0000000..cf26c42 --- /dev/null +++ b/tools/validate_portable_ai_packs.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python3 +"""Validate SourceOS Portable AI Kit carry-pack examples. + +This validator is intentionally stdlib-only. It performs semantic checks for +portable model-carry examples before the contracts are promoted to sourceos-spec. +""" + +from __future__ import annotations + +import json +import sys +from pathlib import Path +from typing import Any + +ROOT = Path(__file__).resolve().parents[1] +PACK_SCHEMA = ROOT / "contracts/model-carry-pack.schema.json" +ROOT_SCHEMA = ROOT / "contracts/portable-ai-root.schema.json" +PACK_EXAMPLES = sorted((ROOT / "examples").glob("model-carry-pack.*.json")) +ROOT_EXAMPLES = sorted((ROOT / "examples").glob("portable-ai-root.*.json")) + +PROFILE_MIN_FREE_GB = { + "tiny-router": 8, + "laptop-safe": 16, + "office-local": 32, + "code-local": 32, + "field-kit": 64, + "byom-gguf": 8, +} + +ALLOWED_PROFILES = set(PROFILE_MIN_FREE_GB) +ALLOWED_SURFACES = {"turtleterm", "agent-term", "bearbrowser", "local-web", "anythingllm-adapter"} + + +class ValidationError(Exception): + pass + + +def load_json(path: Path) -> Any: + try: + return json.loads(path.read_text(encoding="utf-8")) + except FileNotFoundError as exc: + raise ValidationError(f"missing file: {path.relative_to(ROOT)}") from exc + except json.JSONDecodeError as exc: + raise ValidationError(f"invalid JSON in {path.relative_to(ROOT)}: {exc}") from exc + + +def require(condition: bool, message: str) -> None: + if not condition: + raise ValidationError(message) + + +def validate_pack(path: Path, pack: dict[str, Any]) -> None: + rel = path.relative_to(ROOT) + require(pack.get("schemaVersion") == "v0.1", f"{rel}: schemaVersion must be v0.1") + require(pack.get("kind") == "ModelCarryPack", f"{rel}: kind must be ModelCarryPack") + require(str(pack.get("packId", "")).startswith("urn:srcos:model-carry-pack:"), f"{rel}: packId must be a SourceOS model-carry-pack URN") + + profile = pack.get("profileKey") + require(profile in ALLOWED_PROFILES, f"{rel}: unsupported profileKey") + + model = pack.get("model", {}) + require(model.get("name"), f"{rel}: model.name is required") + require(model.get("format") in {"ollama-ref", "gguf", "runtime-managed", "openai-compatible-local", "other"}, f"{rel}: unsupported model.format") + + runtime = pack.get("runtimeCompatibility", []) + require(isinstance(runtime, list) and runtime, f"{rel}: runtimeCompatibility must be non-empty") + + footprint = pack.get("footprint", {}) + require(footprint.get("minimumFreeGb", 0) >= PROFILE_MIN_FREE_GB[profile], f"{rel}: minimumFreeGb below profile floor") + require(footprint.get("recommendedFreeGb", 0) >= footprint.get("minimumFreeGb", 0), f"{rel}: recommendedFreeGb must be >= minimumFreeGb") + require(footprint.get("recommendedRamGb", 0) >= footprint.get("minimumRamGb", 0), f"{rel}: recommendedRamGb must be >= minimumRamGb") + + provenance = pack.get("provenance", {}) + require(provenance.get("licenseRef"), f"{rel}: licenseRef is required") + sha = provenance.get("sha256") + if sha is not None: + require(isinstance(sha, str) and len(sha) == 64, f"{rel}: sha256 must be a 64-char hex string when present") + + policy = pack.get("policy", {}) + require(policy.get("localOnlyDefault") is True, f"{rel}: localOnlyDefault must be true") + require(policy.get("promptEgressDefault") == "deny", f"{rel}: promptEgressDefault must be deny") + require(policy.get("allowToolUseDefault") is False, f"{rel}: allowToolUseDefault must be false") + require(policy.get("allowNetworkDefault") is False, f"{rel}: allowNetworkDefault must be false") + require(policy.get("requiresExplicitImport") is True, f"{rel}: requiresExplicitImport must be true") + require(policy.get("requiresEvidence") is True, f"{rel}: requiresEvidence must be true") + + if profile == "byom-gguf": + require(provenance.get("sha256RequiredBeforeEligibility") is True, f"{rel}: BYOM must require hash before eligibility") + require(policy.get("eligibleForRoutingBeforeHash") is False, f"{rel}: BYOM must not be route-eligible before hash") + require("byom-unverified" in pack.get("labels", []), f"{rel}: BYOM placeholder must carry byom-unverified label") + + evidence = pack.get("evidence", {}) + require(evidence.get("emitPromptHashOnly") is True, f"{rel}: evidence must be prompt-hash-only") + + +def validate_root(path: Path, root: dict[str, Any]) -> None: + rel = path.relative_to(ROOT) + require(root.get("schemaVersion") == "v0.1", f"{rel}: schemaVersion must be v0.1") + require(root.get("kind") == "PortableAIRoot", f"{rel}: kind must be PortableAIRoot") + require(str(root.get("rootId", "")).startswith("urn:srcos:portable-ai-root:"), f"{rel}: rootId must be a SourceOS portable-ai-root URN") + require(root.get("profileKey") in ALLOWED_PROFILES, f"{rel}: unsupported profileKey") + + dirs = root.get("directories", {}) + for key in ["manifests", "runtimes", "models", "cache", "state", "surfaces", "evidence", "tmp"]: + require(key in dirs and isinstance(dirs[key], str), f"{rel}: directories.{key} is required") + + surfaces = set(root.get("surfaces", [])) + require(bool(surfaces), f"{rel}: surfaces must be non-empty") + require(surfaces.issubset(ALLOWED_SURFACES), f"{rel}: unsupported surface declared") + + policy = root.get("policy", {}) + require(policy.get("promptEgressDefault") == "deny", f"{rel}: prompt egress must default deny") + require(policy.get("runtimeActivation") == "agent-machine-gated", f"{rel}: runtimeActivation must be agent-machine-gated") + require(policy.get("modelDownloads") == "explicit-only", f"{rel}: modelDownloads must be explicit-only") + require(policy.get("bindAddressDefault") == "127.0.0.1", f"{rel}: bindAddressDefault must be loopback") + + evidence = root.get("evidence", {}) + require(evidence.get("promptBodiesStored") is False, f"{rel}: prompt bodies must not be stored in evidence") + + +def main() -> int: + try: + load_json(PACK_SCHEMA) + load_json(ROOT_SCHEMA) + require(PACK_EXAMPLES, "no ModelCarryPack examples found") + require(ROOT_EXAMPLES, "no PortableAIRoot examples found") + for example in PACK_EXAMPLES: + validate_pack(example, load_json(example)) + print(f"ok: {example.relative_to(ROOT)}") + for example in ROOT_EXAMPLES: + validate_root(example, load_json(example)) + print(f"ok: {example.relative_to(ROOT)}") + except ValidationError as exc: + print(f"ERR: {exc}", file=sys.stderr) + return 1 + + print("Portable AI pack validation passed") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) From 180ef34a8c543a669b0492dc34fd834f379790ea Mon Sep 17 00:00:00 2001 From: mdheller <21163552+mdheller@users.noreply.github.com> Date: Tue, 5 May 2026 00:30:06 -0400 Subject: [PATCH 12/12] Validate portable AI carry packs --- Makefile | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 24111fb..7c0165d 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: build test validate dist release-dry-run clean +.PHONY: build test validate validate-portable-ai dist release-dry-run clean BIN := sourceos-ai DIST_DIR := dist @@ -17,7 +17,10 @@ build: test: go test ./... -validate: build +validate-portable-ai: + python3 tools/validate_portable_ai_packs.py + +validate: build validate-portable-ai python3 tools/validate_carry_refs.py bin/$(BIN) carry validate --refs examples bin/$(BIN) doctor --refs examples