coreason-manifest 0.3.0__py3-none-any.whl → 0.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -80,29 +80,30 @@ class ManifestEngineAsync:
80
80
  # Clean up resources if necessary.
81
81
  pass
82
82
 
83
- async def load_and_validate(self, manifest_path: Union[str, Path], source_dir: Union[str, Path]) -> AgentDefinition:
84
- """Loads, validates, and verifies an Agent Manifest asynchronously.
83
+ async def validate_manifest_dict(self, raw_data: dict[str, Any]) -> AgentDefinition:
84
+ """Validates an Agent Manifest dictionary in memory.
85
+
86
+ Performs:
87
+ 1. Normalization (stripping version prefixes)
88
+ 2. Schema Validation
89
+ 3. Model Conversion
90
+ 4. Policy Enforcement
91
+
92
+ Does NOT perform Integrity Check (hashing).
85
93
 
86
94
  Args:
87
- manifest_path: Path to the agent.yaml file.
88
- source_dir: Path to the source code directory.
95
+ raw_data: The raw dictionary of the manifest.
89
96
 
90
97
  Returns:
91
- AgentDefinition: The fully validated and verified agent definition.
98
+ AgentDefinition: The fully validated agent definition.
92
99
 
93
100
  Raises:
94
101
  ManifestSyntaxError: If structure or schema is invalid.
95
102
  PolicyViolationError: If business rules are violated.
96
- IntegrityCompromisedError: If source code hash does not match.
97
- FileNotFoundError: If files are missing.
98
103
  """
99
- manifest_path = Path(manifest_path)
100
- source_dir = Path(source_dir)
101
-
102
- logger.info(f"Validating Agent Manifest: {manifest_path}")
103
-
104
- # 1. Load Raw YAML (I/O)
105
- raw_data = await ManifestLoader.load_raw_from_file_async(manifest_path)
104
+ # 1. Normalization (ensure version string is clean before schema/model validation)
105
+ # We access the static method on ManifestLoader.
106
+ ManifestLoader._normalize_data(raw_data)
106
107
 
107
108
  # 2. Schema Validation
108
109
  logger.debug("Running Schema Validation...")
@@ -128,13 +129,42 @@ class ManifestEngineAsync:
128
129
  logger.info(f"Policy Check: Fail - {duration_ms:.2f}ms")
129
130
  raise
130
131
 
132
+ return cast(AgentDefinition, agent_def)
133
+
134
+ async def load_and_validate(self, manifest_path: Union[str, Path], source_dir: Union[str, Path]) -> AgentDefinition:
135
+ """Loads, validates, and verifies an Agent Manifest asynchronously.
136
+
137
+ Args:
138
+ manifest_path: Path to the agent.yaml file.
139
+ source_dir: Path to the source code directory.
140
+
141
+ Returns:
142
+ AgentDefinition: The fully validated and verified agent definition.
143
+
144
+ Raises:
145
+ ManifestSyntaxError: If structure or schema is invalid.
146
+ PolicyViolationError: If business rules are violated.
147
+ IntegrityCompromisedError: If source code hash does not match.
148
+ FileNotFoundError: If files are missing.
149
+ """
150
+ manifest_path = Path(manifest_path)
151
+ source_dir = Path(source_dir)
152
+
153
+ logger.info(f"Validating Agent Manifest: {manifest_path}")
154
+
155
+ # 1. Load Raw YAML (I/O)
156
+ raw_data = await ManifestLoader.load_raw_from_file_async(manifest_path)
157
+
158
+ # 2. Validate Manifest Dict (Schema, Model, Policy)
159
+ agent_def = await self.validate_manifest_dict(raw_data)
160
+
131
161
  # 5. Integrity Check (Heavy I/O and CPU)
132
162
  logger.debug("Verifying Integrity...")
133
163
  # IntegrityChecker.verify is synchronous and does heavy IO, so we wrap it.
134
164
  await anyio.to_thread.run_sync(IntegrityChecker.verify, agent_def, source_dir, manifest_path)
135
165
 
136
166
  logger.info("Agent validation successful.")
137
- return cast(AgentDefinition, agent_def)
167
+ return agent_def
138
168
 
139
169
 
140
170
  class ManifestEngine:
@@ -179,3 +209,14 @@ class ManifestEngine:
179
209
  AgentDefinition: The fully validated and verified agent definition.
180
210
  """
181
211
  return cast(AgentDefinition, anyio.run(self._async.load_and_validate, manifest_path, source_dir))
212
+
213
+ def validate_manifest_dict(self, raw_data: dict[str, Any]) -> AgentDefinition:
214
+ """Validates an Agent Manifest dictionary synchronously.
215
+
216
+ Args:
217
+ raw_data: The raw dictionary of the manifest.
218
+
219
+ Returns:
220
+ AgentDefinition: The fully validated agent definition.
221
+ """
222
+ return cast(AgentDefinition, anyio.run(self._async.validate_manifest_dict, raw_data))
@@ -0,0 +1,123 @@
1
+ from contextlib import asynccontextmanager
2
+ from importlib import resources
3
+ from pathlib import Path
4
+ from typing import AsyncIterator, List, Optional, Union
5
+
6
+ from fastapi import FastAPI, HTTPException, Request, status
7
+ from fastapi.responses import JSONResponse
8
+ from pydantic import BaseModel, Field
9
+
10
+ from coreason_manifest.engine import ManifestConfig, ManifestEngineAsync
11
+ from coreason_manifest.errors import ManifestSyntaxError, PolicyViolationError
12
+
13
+
14
+ # Response Model
15
+ class ValidationResponse(BaseModel):
16
+ valid: bool
17
+ agent_id: Optional[str] = None
18
+ version: Optional[str] = None
19
+ policy_violations: List[str] = Field(default_factory=list)
20
+
21
+
22
+ @asynccontextmanager
23
+ async def lifespan(app: FastAPI) -> AsyncIterator[None]:
24
+ # Locate policies
25
+ policy_path: Optional[Path] = None
26
+ tbom_path: Optional[Path] = None
27
+
28
+ # 1. Check local directory (Docker runtime with COPY or relative dev path)
29
+ # In Docker we will COPY to /app/policies/ or ./policies/ relative to WORKDIR
30
+ # We'll check common locations.
31
+ possible_dirs = [
32
+ Path("policies"),
33
+ Path("/app/policies"),
34
+ Path("src/coreason_manifest/policies"), # Dev from root
35
+ ]
36
+
37
+ for d in possible_dirs:
38
+ if (d / "compliance.rego").exists():
39
+ policy_path = d / "compliance.rego"
40
+ if (d / "tbom.json").exists():
41
+ tbom_path = d / "tbom.json"
42
+ break
43
+
44
+ # 2. Fallback to package resources
45
+ resource_context = None
46
+ if not policy_path:
47
+ try:
48
+ # Check if it exists as a resource
49
+ ref = resources.files("coreason_manifest.policies").joinpath("compliance.rego")
50
+ if ref.is_file():
51
+ resource_context = resources.as_file(ref)
52
+ policy_path = resource_context.__enter__()
53
+ # Check for TBOM in same dir if possible, or ignore for fallback
54
+ except Exception:
55
+ pass
56
+
57
+ # If still not found, fail.
58
+ if not policy_path:
59
+ raise RuntimeError("Could not locate compliance.rego policy file.")
60
+
61
+ config = ManifestConfig(
62
+ policy_path=policy_path,
63
+ tbom_path=tbom_path,
64
+ opa_path="opa", # Assumes OPA is in PATH (installed via Dockerfile)
65
+ )
66
+
67
+ engine = ManifestEngineAsync(config)
68
+
69
+ try:
70
+ async with engine:
71
+ app.state.engine = engine
72
+ yield
73
+ finally:
74
+ if resource_context:
75
+ resource_context.__exit__(None, None, None)
76
+
77
+
78
+ app = FastAPI(lifespan=lifespan)
79
+
80
+
81
+ @app.post("/validate", response_model=ValidationResponse) # type: ignore[misc]
82
+ async def validate_manifest(request: Request) -> Union[ValidationResponse, JSONResponse]:
83
+ engine: ManifestEngineAsync = app.state.engine
84
+
85
+ try:
86
+ raw_body = await request.json()
87
+ except Exception:
88
+ raise HTTPException(status_code=400, detail="Invalid JSON body") from None
89
+
90
+ try:
91
+ agent_def = await engine.validate_manifest_dict(raw_body)
92
+ return ValidationResponse(
93
+ valid=True,
94
+ agent_id=str(agent_def.metadata.id),
95
+ version=agent_def.metadata.version,
96
+ policy_violations=[],
97
+ )
98
+ except ManifestSyntaxError as e:
99
+ # Return 422 with the error
100
+ resp = ValidationResponse(valid=False, policy_violations=[f"Syntax Error: {str(e)}"])
101
+ return JSONResponse(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, content=resp.model_dump())
102
+ except PolicyViolationError as e:
103
+ resp = ValidationResponse(valid=False, policy_violations=e.violations)
104
+ return JSONResponse(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, content=resp.model_dump())
105
+
106
+
107
+ @app.get("/health") # type: ignore[misc]
108
+ async def health_check() -> dict[str, str]:
109
+ engine: ManifestEngineAsync = app.state.engine
110
+ policy_version = "unknown"
111
+ try:
112
+ import hashlib
113
+
114
+ # policy_path is guaranteed to exist by lifespan check
115
+ policy_path = Path(engine.config.policy_path)
116
+ if policy_path.exists():
117
+ with open(policy_path, "rb") as f:
118
+ digest = hashlib.sha256(f.read()).hexdigest()[:8]
119
+ policy_version = digest
120
+ except Exception:
121
+ pass
122
+
123
+ return {"status": "active", "policy_version": policy_version}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: coreason_manifest
3
- Version: 0.3.0
3
+ Version: 0.4.0
4
4
  Summary: This package is the definitive source of truth. If it isn't in the manifest, it doesn't exist. If it violates the manifest, it doesn't run.
5
5
  License: # The Prosperity Public License 3.0.0
6
6
 
@@ -69,11 +69,13 @@ Classifier: Programming Language :: Python :: 3.12
69
69
  Classifier: Operating System :: OS Independent
70
70
  Requires-Dist: aiofiles (>=23.2.1,<24.0.0)
71
71
  Requires-Dist: anyio (>=4.3.0,<5.0.0)
72
+ Requires-Dist: fastapi (>=0.111.0,<0.112.0)
72
73
  Requires-Dist: httpx (>=0.27.0,<0.28.0)
73
74
  Requires-Dist: jsonschema (>=4.25.1,<5.0.0)
74
75
  Requires-Dist: loguru (>=0.7.2,<0.8.0)
75
76
  Requires-Dist: pydantic (>=2.12.5,<3.0.0)
76
77
  Requires-Dist: pyyaml (>=6.0.3,<7.0.0)
78
+ Requires-Dist: uvicorn (>=0.30.1,<0.31.0)
77
79
  Project-URL: Documentation, https://github.com/CoReason-AI/coreason_manifest
78
80
  Project-URL: Homepage, https://github.com/CoReason-AI/coreason_manifest
79
81
  Project-URL: Repository, https://github.com/CoReason-AI/coreason_manifest
@@ -99,6 +101,7 @@ The definitive source of truth for CoReason-AI Asset definitions. "The Blueprint
99
101
  * **Integrity Verification:** Calculates and verifies SHA256 hashes of the agent's source code to prevent tampering.
100
102
  * **Dependency Pinning:** Enforces strict version pinning for all library dependencies.
101
103
  * **Trusted Bill of Materials (TBOM):** Validates libraries against an approved list.
104
+ * **Compliance Microservice:** Can be run as a standalone API server (Service C) for centralized validation.
102
105
 
103
106
  ## Installation
104
107
 
@@ -108,30 +111,30 @@ pip install coreason-manifest
108
111
 
109
112
  ## Usage
110
113
 
111
- Here is how to initialize the engine and validate an agent manifest:
114
+ `coreason-manifest` supports two modes: **Library (CLI)** and **Server (Microservice)**.
115
+
116
+ ### 1. Library Usage
117
+
118
+ Use the python library to validate local agent files and verify source integrity.
112
119
 
113
120
  ```python
114
- from coreason_manifest import ManifestEngine, ManifestConfig, PolicyViolationError, IntegrityCompromisedError
121
+ from coreason_manifest import ManifestEngine, ManifestConfig
115
122
 
116
- # 1. Initialize configuration with policy path
117
- config = ManifestConfig(policy_path="./policies/gx_compliant.rego")
123
+ # Initialize and Validate
124
+ config = ManifestConfig(policy_path="./policies/compliance.rego")
118
125
  engine = ManifestEngine(config)
126
+ agent_def = engine.load_and_validate("agent.yaml", "./src")
127
+ ```
119
128
 
120
- # 2. Load & Validate Agent Manifest
121
- try:
122
- # This runs Schema Validation, Policy Enforcement, and Integrity Checks
123
- agent_def = engine.load_and_validate(
124
- manifest_path="./agents/payer_war_game/agent.yaml",
125
- source_dir="./agents/payer_war_game/src"
126
- )
127
- print(f"Agent {agent_def.metadata.name} is compliant and ready to run.")
129
+ ### 2. Server Mode
128
130
 
129
- except PolicyViolationError as e:
130
- print(f"Compliance Failure: {e.violations}")
131
+ Run the package as a FastAPI server to provide a centralized compliance API.
131
132
 
132
- except IntegrityCompromisedError:
133
- print("CRITICAL: Code has been tampered with after signing.")
133
+ ```bash
134
+ uvicorn coreason_manifest.server:app --host 0.0.0.0 --port 8000
134
135
  ```
135
136
 
136
- For detailed requirements and architecture, please refer to the [Product Requirements](docs/product_requirements.md).
137
+ For full details, see the [Usage Documentation](docs/usage.md).
138
+
139
+ For detailed requirements and architecture, please refer to the [Product Requirements](docs/product_requirements.md) or [Requirements](docs/requirements.md).
137
140
 
@@ -1,5 +1,5 @@
1
1
  coreason_manifest/__init__.py,sha256=R07xUHw9RWdWQPh7OWbpMT8kkzkEWfEI5hizu_5UNck,1528
2
- coreason_manifest/engine.py,sha256=zR_O2N-olwL1HSjAq03Um3qr-jbzmaIhOA19tpY0tFs,6753
2
+ coreason_manifest/engine.py,sha256=ENAkRuGFd1uY_5u4EYEsTZUMrqw5HgijKtkF9lk-E-c,8199
3
3
  coreason_manifest/errors.py,sha256=CKFWSucncoPLYUJcsuXtLIfneyr2d89oHRTiWPT4psU,1437
4
4
  coreason_manifest/integrity.py,sha256=CMBaa2A8DpC9hDSO-aj-YOzss6if1CajaeLEmTC703c,5344
5
5
  coreason_manifest/loader.py,sha256=cc1iV-yKfH3gyPaB-P_7AaTaPYkgj1NoDsJz4PReTgo,5479
@@ -10,11 +10,12 @@ coreason_manifest/policies/tbom.json,sha256=rSn4V44_IdFqCC86J3Jc31qQKTV4J5BdmyO0
10
10
  coreason_manifest/policy.py,sha256=vvEivq5HSjv-bMSrZ5VMM3eTomYFp39SBtuFajG9RU4,5009
11
11
  coreason_manifest/schemas/__init__.py,sha256=9TMs6jWKCIewAKkj-u0tq9c6N_-0CU6b5s9q6MTS6v4,17
12
12
  coreason_manifest/schemas/agent.schema.json,sha256=wdNYmpyCUEKR1UgMmRV4ZSQOm7xvnmSg5lvhQ3JKvSg,6368
13
+ coreason_manifest/server.py,sha256=kHrReqoEx4sL5k5GQN7yNwLgvfnOl1kZf-dQehseuO4,4251
13
14
  coreason_manifest/utils/__init__.py,sha256=Q9gXiBtX3mD9GTu4z0JDHSHkbXC-MRHagrOaOmRH_1Q,435
14
15
  coreason_manifest/utils/logger.py,sha256=A7E6Hd_Jk1XDUajNEJQl-WtUv9M2LT76b4_TsbxnILw,1227
15
16
  coreason_manifest/validator.py,sha256=v33EzKroRwLEjZeuRRpTB7cqB38op3DV2EZpZhJ80a0,2240
16
- coreason_manifest-0.3.0.dist-info/METADATA,sha256=7a-XR2oNa4OqWlD9RqkuOc7B709pHJfu7BsKoHJxRis,7176
17
- coreason_manifest-0.3.0.dist-info/WHEEL,sha256=3ny-bZhpXrU6vSQ1UPG34FoxZBp3lVcvK0LkgUz6VLk,88
18
- coreason_manifest-0.3.0.dist-info/licenses/LICENSE,sha256=3tYb7ZQe7sVXcbNmX22fDESFjOSIlCZodUGpZMkuSlk,3063
19
- coreason_manifest-0.3.0.dist-info/licenses/NOTICE,sha256=tqzUyP9VTCGxoHLgBI0AC1i0G7m_PSyESFL8Jwuw0dA,610
20
- coreason_manifest-0.3.0.dist-info/RECORD,,
17
+ coreason_manifest-0.4.0.dist-info/METADATA,sha256=aQdNN6nn8Fi4g_eSj17mKrtf_SkO4l0XembzP85TXlE,7229
18
+ coreason_manifest-0.4.0.dist-info/WHEEL,sha256=3ny-bZhpXrU6vSQ1UPG34FoxZBp3lVcvK0LkgUz6VLk,88
19
+ coreason_manifest-0.4.0.dist-info/licenses/LICENSE,sha256=3tYb7ZQe7sVXcbNmX22fDESFjOSIlCZodUGpZMkuSlk,3063
20
+ coreason_manifest-0.4.0.dist-info/licenses/NOTICE,sha256=tqzUyP9VTCGxoHLgBI0AC1i0G7m_PSyESFL8Jwuw0dA,610
21
+ coreason_manifest-0.4.0.dist-info/RECORD,,