evidentia-api 0.6.0__tar.gz

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.
Files changed (27) hide show
  1. evidentia_api-0.6.0/.gitignore +95 -0
  2. evidentia_api-0.6.0/PKG-INFO +95 -0
  3. evidentia_api-0.6.0/README.md +66 -0
  4. evidentia_api-0.6.0/hatch_build.py +127 -0
  5. evidentia_api-0.6.0/pyproject.toml +54 -0
  6. evidentia_api-0.6.0/src/evidentia_api/__init__.py +21 -0
  7. evidentia_api-0.6.0/src/evidentia_api/app.py +236 -0
  8. evidentia_api-0.6.0/src/evidentia_api/cli.py +123 -0
  9. evidentia_api-0.6.0/src/evidentia_api/deps.py +25 -0
  10. evidentia_api-0.6.0/src/evidentia_api/routers/__init__.py +5 -0
  11. evidentia_api-0.6.0/src/evidentia_api/routers/collectors.py +151 -0
  12. evidentia_api-0.6.0/src/evidentia_api/routers/config.py +74 -0
  13. evidentia_api-0.6.0/src/evidentia_api/routers/doctor.py +204 -0
  14. evidentia_api-0.6.0/src/evidentia_api/routers/explain.py +99 -0
  15. evidentia_api-0.6.0/src/evidentia_api/routers/frameworks.py +92 -0
  16. evidentia_api-0.6.0/src/evidentia_api/routers/gaps.py +190 -0
  17. evidentia_api-0.6.0/src/evidentia_api/routers/health.py +46 -0
  18. evidentia_api-0.6.0/src/evidentia_api/routers/init_wizard.py +67 -0
  19. evidentia_api-0.6.0/src/evidentia_api/routers/integrations.py +200 -0
  20. evidentia_api-0.6.0/src/evidentia_api/routers/llm_status.py +62 -0
  21. evidentia_api-0.6.0/src/evidentia_api/routers/risks.py +206 -0
  22. evidentia_api-0.6.0/src/evidentia_api/schemas.py +192 -0
  23. evidentia_api-0.6.0/src/evidentia_api/static/.gitkeep +6 -0
  24. evidentia_api-0.6.0/src/evidentia_api/static/assets/index-BfU73xHx.js +126 -0
  25. evidentia_api-0.6.0/src/evidentia_api/static/assets/index-BfU73xHx.js.map +1 -0
  26. evidentia_api-0.6.0/src/evidentia_api/static/assets/index-CFl0YLQ6.css +1 -0
  27. evidentia_api-0.6.0/src/evidentia_api/static/index.html +19 -0
@@ -0,0 +1,95 @@
1
+ # v0.4.0 — frontend build output lands in the Python package's static
2
+ # directory at wheel-assembly time via the hatchling build hook. The
3
+ # .gitkeep file in static/ is tracked; everything else is regenerated.
4
+ packages/evidentia-api/src/evidentia_api/static/assets/
5
+ packages/evidentia-api/src/evidentia_api/static/index.html
6
+ packages/evidentia-api/src/evidentia_api/static/*.js
7
+ packages/evidentia-api/src/evidentia_api/static/*.css
8
+
9
+ # Python
10
+ __pycache__/
11
+ *.py[cod]
12
+ *$py.class
13
+ *.so
14
+ .Python
15
+ build/
16
+ develop-eggs/
17
+ dist/
18
+ downloads/
19
+ eggs/
20
+ .eggs/
21
+ lib/
22
+ lib64/
23
+ parts/
24
+ sdist/
25
+ var/
26
+ wheels/
27
+ # NB: `lib/` and `lib64/` above would otherwise also match
28
+ # packages/evidentia-ui/src/lib/ (TypeScript utils). Scope to top-level
29
+ # only — there's no real Python-venv lib/ we'd fail to ignore because
30
+ # .venv/ and venv/ below cover that case.
31
+ !packages/evidentia-ui/src/lib/
32
+ *.egg-info/
33
+ .installed.cfg
34
+ *.egg
35
+ MANIFEST
36
+
37
+ # Virtual environments
38
+ .venv/
39
+ venv/
40
+ ENV/
41
+ env/
42
+
43
+ # uv
44
+ # NOTE: uv.lock is committed for reproducible builds.
45
+ # https://docs.astral.sh/uv/concepts/projects/sync/#locking-dependencies
46
+
47
+ # Testing
48
+ .pytest_cache/
49
+ .coverage
50
+ .coverage.*
51
+ htmlcov/
52
+ .tox/
53
+ .cache
54
+ coverage.xml
55
+ *.cover
56
+ .hypothesis/
57
+
58
+ # mypy
59
+ .mypy_cache/
60
+ .dmypy.json
61
+ dmypy.json
62
+
63
+ # Ruff
64
+ .ruff_cache/
65
+
66
+ # IDE
67
+ .vscode/
68
+ .idea/
69
+ *.swp
70
+ *.swo
71
+ *~
72
+ .DS_Store
73
+
74
+ # Claude Code local state
75
+ .claude/
76
+
77
+ # Evidentia runtime — user project state (NOT bundled examples).
78
+ # `.controlbridge/` and `/controlbridge.yaml` are kept ignored for the
79
+ # lifetime of the shim (through v0.7.0) so legacy project workspaces
80
+ # authored against v0.1.0 – v0.5.0 don't start leaking into git.
81
+ .evidentia/
82
+ .controlbridge/
83
+ /evidentia.yaml
84
+ /controlbridge.yaml
85
+ *.local.yaml
86
+ evidence/
87
+ reports/
88
+ risks/
89
+
90
+ # Generated reports from examples (keep source files, ignore generated ones)
91
+ examples/**/report.json
92
+ examples/**/report.csv
93
+ examples/**/report.md
94
+ examples/**/report.oscal.json
95
+ examples/**/risks.json
@@ -0,0 +1,95 @@
1
+ Metadata-Version: 2.4
2
+ Name: evidentia-api
3
+ Version: 0.6.0
4
+ Summary: FastAPI REST server + bundled React web UI for Evidentia
5
+ Project-URL: Homepage, https://github.com/allenfbyrd/evidentia
6
+ Project-URL: Repository, https://github.com/allenfbyrd/evidentia
7
+ Project-URL: Issues, https://github.com/allenfbyrd/evidentia/issues
8
+ Project-URL: Changelog, https://github.com/allenfbyrd/evidentia/blob/main/CHANGELOG.md
9
+ Author-email: Allen Byrd <allen@allenfbyrd.com>
10
+ License-Expression: Apache-2.0
11
+ Keywords: compliance,fastapi,grc,oscal,rest-api,web-ui
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Framework :: FastAPI
14
+ Classifier: Intended Audience :: Information Technology
15
+ Classifier: License :: OSI Approved :: Apache Software License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
19
+ Classifier: Topic :: Security
20
+ Classifier: Typing :: Typed
21
+ Requires-Python: >=3.12
22
+ Requires-Dist: evidentia-ai<0.7.0,>=0.6.0
23
+ Requires-Dist: evidentia-core<0.7.0,>=0.6.0
24
+ Requires-Dist: fastapi>=0.115
25
+ Requires-Dist: python-multipart>=0.0.9
26
+ Requires-Dist: sse-starlette>=2.1
27
+ Requires-Dist: uvicorn[standard]>=0.30
28
+ Description-Content-Type: text/markdown
29
+
30
+ # evidentia-api
31
+
32
+ FastAPI REST server and bundled React web UI for **Evidentia**, the open-source GRC tool.
33
+
34
+ This package is not typically installed directly. The preferred way is via the `[gui]` extra on the meta-package:
35
+
36
+ ```bash
37
+ uv tool install "evidentia[gui]"
38
+ # or
39
+ pip install "evidentia[gui]"
40
+ ```
41
+
42
+ Then run:
43
+
44
+ ```bash
45
+ evidentia serve
46
+ # -> FastAPI + React UI at http://127.0.0.1:8000
47
+ ```
48
+
49
+ ## What's inside
50
+
51
+ - **FastAPI app** (`evidentia_api.app:app`) — REST endpoints mirroring every CLI capability.
52
+ - **SPA** — React/Vite/shadcn/ui frontend, bundled as static assets inside the wheel under `evidentia_api/static/`.
53
+ - **SSE streaming** — long-running LLM calls (`risk generate`, `explain`) stream progress to the browser without blocking.
54
+
55
+ ## REST surface
56
+
57
+ Every endpoint is typed with Pydantic models reused from `evidentia-core`. All endpoints bind to `127.0.0.1` by default; `--host 0.0.0.0` emits a security warning.
58
+
59
+ | Method | Path | Purpose |
60
+ |---|---|---|
61
+ | GET | `/api/health` | Health probe |
62
+ | GET | `/api/version` | Evidentia version info |
63
+ | GET | `/api/doctor` | Diagnostic summary |
64
+ | POST | `/api/doctor/check-air-gap` | Air-gap validator |
65
+ | GET | `/api/config` | Read `evidentia.yaml` |
66
+ | PUT | `/api/config` | Write `evidentia.yaml` |
67
+ | GET | `/api/frameworks` | List all 82 bundled catalogs |
68
+ | GET | `/api/frameworks/{id}` | Framework detail |
69
+ | GET | `/api/frameworks/{id}/controls/{control_id}` | Single control |
70
+ | POST | `/api/gap/analyze` | Run GapAnalyzer, save to gap store |
71
+ | GET | `/api/gap/reports` | List saved reports |
72
+ | GET | `/api/gap/reports/{key}` | Load a saved report |
73
+ | POST | `/api/gap/diff` | Compute diff between two reports |
74
+ | POST | `/api/risk/generate` | SSE: per-gap risk statement generation |
75
+ | POST | `/api/explain/{framework}/{control_id}` | Plain-English control explanation |
76
+ | POST | `/api/init/wizard` | Generate starter YAML files |
77
+ | GET | `/api/llm-status` | LLM provider configuration state |
78
+
79
+ ## Air-gapped mode
80
+
81
+ Running `evidentia serve --offline` wires the air-gap guard into every `/api/*` call. LLM features gracefully degrade with a pointer to Ollama. See [`docs/air-gapped.md`](../../docs/air-gapped.md).
82
+
83
+ ## Development
84
+
85
+ ```bash
86
+ # From the repo root:
87
+ uv sync --all-packages
88
+ cd packages/evidentia-ui && npm install && npm run dev # Vite dev server at :5173
89
+ # In another terminal:
90
+ evidentia serve --dev # FastAPI at :8000 proxies /api/* to itself, / to Vite :5173
91
+ ```
92
+
93
+ ## License
94
+
95
+ Apache-2.0 — see [`LICENSE`](../../LICENSE).
@@ -0,0 +1,66 @@
1
+ # evidentia-api
2
+
3
+ FastAPI REST server and bundled React web UI for **Evidentia**, the open-source GRC tool.
4
+
5
+ This package is not typically installed directly. The preferred way is via the `[gui]` extra on the meta-package:
6
+
7
+ ```bash
8
+ uv tool install "evidentia[gui]"
9
+ # or
10
+ pip install "evidentia[gui]"
11
+ ```
12
+
13
+ Then run:
14
+
15
+ ```bash
16
+ evidentia serve
17
+ # -> FastAPI + React UI at http://127.0.0.1:8000
18
+ ```
19
+
20
+ ## What's inside
21
+
22
+ - **FastAPI app** (`evidentia_api.app:app`) — REST endpoints mirroring every CLI capability.
23
+ - **SPA** — React/Vite/shadcn/ui frontend, bundled as static assets inside the wheel under `evidentia_api/static/`.
24
+ - **SSE streaming** — long-running LLM calls (`risk generate`, `explain`) stream progress to the browser without blocking.
25
+
26
+ ## REST surface
27
+
28
+ Every endpoint is typed with Pydantic models reused from `evidentia-core`. All endpoints bind to `127.0.0.1` by default; `--host 0.0.0.0` emits a security warning.
29
+
30
+ | Method | Path | Purpose |
31
+ |---|---|---|
32
+ | GET | `/api/health` | Health probe |
33
+ | GET | `/api/version` | Evidentia version info |
34
+ | GET | `/api/doctor` | Diagnostic summary |
35
+ | POST | `/api/doctor/check-air-gap` | Air-gap validator |
36
+ | GET | `/api/config` | Read `evidentia.yaml` |
37
+ | PUT | `/api/config` | Write `evidentia.yaml` |
38
+ | GET | `/api/frameworks` | List all 82 bundled catalogs |
39
+ | GET | `/api/frameworks/{id}` | Framework detail |
40
+ | GET | `/api/frameworks/{id}/controls/{control_id}` | Single control |
41
+ | POST | `/api/gap/analyze` | Run GapAnalyzer, save to gap store |
42
+ | GET | `/api/gap/reports` | List saved reports |
43
+ | GET | `/api/gap/reports/{key}` | Load a saved report |
44
+ | POST | `/api/gap/diff` | Compute diff between two reports |
45
+ | POST | `/api/risk/generate` | SSE: per-gap risk statement generation |
46
+ | POST | `/api/explain/{framework}/{control_id}` | Plain-English control explanation |
47
+ | POST | `/api/init/wizard` | Generate starter YAML files |
48
+ | GET | `/api/llm-status` | LLM provider configuration state |
49
+
50
+ ## Air-gapped mode
51
+
52
+ Running `evidentia serve --offline` wires the air-gap guard into every `/api/*` call. LLM features gracefully degrade with a pointer to Ollama. See [`docs/air-gapped.md`](../../docs/air-gapped.md).
53
+
54
+ ## Development
55
+
56
+ ```bash
57
+ # From the repo root:
58
+ uv sync --all-packages
59
+ cd packages/evidentia-ui && npm install && npm run dev # Vite dev server at :5173
60
+ # In another terminal:
61
+ evidentia serve --dev # FastAPI at :8000 proxies /api/* to itself, / to Vite :5173
62
+ ```
63
+
64
+ ## License
65
+
66
+ Apache-2.0 — see [`LICENSE`](../../LICENSE).
@@ -0,0 +1,127 @@
1
+ """Hatchling build hook — bundle the Vite-built React SPA into the wheel.
2
+
3
+ Runs before wheel packaging when ``uv build`` (or ``python -m build``) is
4
+ invoked for the ``evidentia-api`` package:
5
+
6
+ 1. Checks if ``packages/evidentia-ui/dist/`` exists and is non-empty.
7
+ 2. If yes, copies ``dist/*`` into ``src/evidentia_api/static/``.
8
+ 3. If no, runs ``npm ci && npm run build`` in the UI directory to produce
9
+ ``dist/`` first, then copies. Gracefully skips with a warning if Node
10
+ isn't installed on the build machine — for Python-only contributors
11
+ who only touch backend code.
12
+
13
+ The hook is a no-op on machines that set the ``EVIDENTIA_SKIP_FRONTEND_BUILD``
14
+ environment variable — convenient for CI matrices that separate concerns
15
+ between Python tests and frontend builds.
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import logging
21
+ import os
22
+ import shutil
23
+ import subprocess
24
+ from pathlib import Path
25
+ from typing import Any
26
+
27
+ from hatchling.builders.hooks.plugin.interface import BuildHookInterface
28
+
29
+ logger = logging.getLogger("hatch.build.evidentia-api")
30
+
31
+ # Paths are resolved relative to the evidentia-api package root at
32
+ # build time. The UI sits one directory up.
33
+ _PKG_ROOT = Path(__file__).parent
34
+ _UI_DIR = (_PKG_ROOT.parent / "evidentia-ui").resolve()
35
+ _UI_DIST = _UI_DIR / "dist"
36
+ _STATIC_DEST = _PKG_ROOT / "src" / "evidentia_api" / "static"
37
+
38
+
39
+ class FrontendBundleHook(BuildHookInterface):
40
+ """Copies the React SPA build output into the Python package tree."""
41
+
42
+ PLUGIN_NAME = "evidentia-frontend"
43
+
44
+ def initialize(self, version: str, build_data: dict[str, Any]) -> None:
45
+ """Invoked by hatchling before the wheel/sdist is assembled."""
46
+ del version, build_data # unused
47
+
48
+ if os.environ.get("EVIDENTIA_SKIP_FRONTEND_BUILD"):
49
+ logger.info(
50
+ "EVIDENTIA_SKIP_FRONTEND_BUILD set; leaving static/ untouched."
51
+ )
52
+ return
53
+
54
+ if not _UI_DIR.is_dir():
55
+ logger.warning(
56
+ "Frontend dir %s not found; skipping static bundle. "
57
+ "The wheel will serve a dev-placeholder page.",
58
+ _UI_DIR,
59
+ )
60
+ return
61
+
62
+ # If the user hasn't run `npm run build` yet, try to do it now.
63
+ if (not _UI_DIST.is_dir() or not any(_UI_DIST.iterdir())) and not _try_npm_build():
64
+ # Node unavailable or build failed. Ship without the SPA.
65
+ logger.warning(
66
+ "Frontend build unavailable; static/ will remain empty. "
67
+ "The wheel will serve a dev-placeholder page."
68
+ )
69
+ return
70
+
71
+ self._copy_dist_to_static()
72
+
73
+ def _copy_dist_to_static(self) -> None:
74
+ """Sync ``dist/*`` -> ``static/``. Wipes stale assets first."""
75
+ _STATIC_DEST.mkdir(parents=True, exist_ok=True)
76
+
77
+ # Clear stale assets but keep the .gitkeep file so the directory
78
+ # is preserved even when fresh checkouts haven't built yet.
79
+ for item in _STATIC_DEST.iterdir():
80
+ if item.name == ".gitkeep":
81
+ continue
82
+ if item.is_file() or item.is_symlink():
83
+ item.unlink()
84
+ else:
85
+ shutil.rmtree(item)
86
+
87
+ for src in _UI_DIST.iterdir():
88
+ dest = _STATIC_DEST / src.name
89
+ if src.is_dir():
90
+ shutil.copytree(src, dest)
91
+ else:
92
+ shutil.copy2(src, dest)
93
+
94
+ logger.info("Copied frontend bundle from %s -> %s", _UI_DIST, _STATIC_DEST)
95
+
96
+
97
+ def _try_npm_build() -> bool:
98
+ """Attempt ``npm install && npm run build`` in the UI dir. Return True on success.
99
+
100
+ Uses ``npm ci`` when ``package-lock.json`` already exists (faster + deterministic)
101
+ and falls back to ``npm install`` when it doesn't — this covers fresh checkouts
102
+ where the lockfile hasn't been committed yet.
103
+ """
104
+ npm = shutil.which("npm")
105
+ if npm is None:
106
+ logger.warning(
107
+ "`npm` not found on PATH; cannot auto-build frontend. "
108
+ "Install Node 20+ or run `npm install && npm run build` manually "
109
+ "in packages/evidentia-ui/ before `uv build`."
110
+ )
111
+ return False
112
+
113
+ lockfile = _UI_DIR / "package-lock.json"
114
+ install_cmd = (
115
+ [npm, "ci", "--no-audit", "--no-fund"]
116
+ if lockfile.is_file()
117
+ else [npm, "install", "--no-audit", "--no-fund"]
118
+ )
119
+
120
+ for cmd in (install_cmd, [npm, "run", "build"]):
121
+ logger.info("Running: %s (cwd=%s)", " ".join(cmd), _UI_DIR)
122
+ try:
123
+ subprocess.run(cmd, cwd=_UI_DIR, check=True)
124
+ except subprocess.CalledProcessError as e:
125
+ logger.error("Frontend build step failed: %s", e)
126
+ return False
127
+ return True
@@ -0,0 +1,54 @@
1
+ [project]
2
+ name = "evidentia-api"
3
+ version = "0.6.0"
4
+ description = "FastAPI REST server + bundled React web UI for Evidentia"
5
+ readme = "README.md"
6
+ authors = [{name = "Allen Byrd", email = "allen@allenfbyrd.com"}]
7
+ license = "Apache-2.0"
8
+ requires-python = ">=3.12"
9
+ keywords = ["grc", "compliance", "fastapi", "web-ui", "oscal", "rest-api"]
10
+ classifiers = [
11
+ "Development Status :: 3 - Alpha",
12
+ "Intended Audience :: Information Technology",
13
+ "License :: OSI Approved :: Apache Software License",
14
+ "Operating System :: OS Independent",
15
+ "Programming Language :: Python :: 3.12",
16
+ "Topic :: Internet :: WWW/HTTP :: HTTP Servers",
17
+ "Topic :: Security",
18
+ "Framework :: FastAPI",
19
+ "Typing :: Typed",
20
+ ]
21
+ dependencies = [
22
+ "evidentia-core>=0.6.0,<0.7.0",
23
+ "evidentia-ai>=0.6.0,<0.7.0",
24
+ "fastapi>=0.115",
25
+ "uvicorn[standard]>=0.30",
26
+ "python-multipart>=0.0.9",
27
+ "sse-starlette>=2.1",
28
+ ]
29
+
30
+ [project.urls]
31
+ Homepage = "https://github.com/allenfbyrd/evidentia"
32
+ Repository = "https://github.com/allenfbyrd/evidentia"
33
+ Issues = "https://github.com/allenfbyrd/evidentia/issues"
34
+ Changelog = "https://github.com/allenfbyrd/evidentia/blob/main/CHANGELOG.md"
35
+
36
+ [build-system]
37
+ requires = ["hatchling"]
38
+ build-backend = "hatchling.build"
39
+
40
+ [tool.hatch.build.targets.wheel]
41
+ packages = ["src/evidentia_api"]
42
+ # The static/ subdirectory is populated at build time by the custom
43
+ # hatchling build hook (hatch_build.py). The hook runs `npm run build`
44
+ # in packages/evidentia-ui/ and copies dist/* into
45
+ # src/evidentia_api/static/ before wheel assembly. Set the env var
46
+ # EVIDENTIA_SKIP_FRONTEND_BUILD=1 to bypass the hook in Python-only
47
+ # build environments.
48
+
49
+ [tool.hatch.build.hooks.custom]
50
+ path = "hatch_build.py"
51
+
52
+ [tool.uv.sources]
53
+ evidentia-core = { workspace = true }
54
+ evidentia-ai = { workspace = true }
@@ -0,0 +1,21 @@
1
+ """Evidentia API: FastAPI REST server + bundled React web UI.
2
+
3
+ The FastAPI application is exposed as :data:`evidentia_api.app.app` and
4
+ can be served directly with uvicorn:
5
+
6
+ uvicorn evidentia_api.app:app --host 127.0.0.1 --port 8000
7
+
8
+ Typical users reach it via the CLI:
9
+
10
+ evidentia serve [--host HOST] [--port PORT] [--offline]
11
+
12
+ which is a thin Typer wrapper around :func:`evidentia_api.cli.serve`.
13
+ """
14
+
15
+ from importlib.metadata import PackageNotFoundError
16
+ from importlib.metadata import version as _pkg_version
17
+
18
+ try:
19
+ __version__ = _pkg_version("evidentia-api")
20
+ except PackageNotFoundError: # pragma: no cover — only hit in editable repos without install
21
+ __version__ = "0.0.0+unknown"