Skip to content

Commit cfac152

Browse files
committed
Add bundle manifest schema
1 parent 4dd4d78 commit cfac152

7 files changed

Lines changed: 101 additions & 4 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
- Added `openexit run` to execute assessment, mapping, full generation, validation, status reporting, and optional evidence bundle export for collected projects.
3636
- Added `openexit demo` with embedded redacted fixtures for complete release-binary demos across supported assessment paths.
3737
- Added a machine-readable evidence bundle manifest with build metadata, validation summary, and per-file SHA-256 digests.
38+
- Added a public JSON Schema for evidence bundle manifests and test coverage that validates exported manifests against it.
3839
- Updated CI and release workflows to Node.js 24-native GitHub Actions.
3940

4041
## 0.1.0 - 2026-05-24

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ Included in the current implementation:
118118
- ArgoCD starter manifest.
119119
- Typed migration plan manifest and phase-gate Markdown plan.
120120
- Validation report with embedded JSON Schema checks, Grafana dashboard, Prometheus alert, OpenTelemetry collector, ArgoCD, Forgejo migration, identity realm/client, edge VCL/HAProxy/Coraza, and LiteLLM/vLLM candidate checks, YAML/JSON parsing, evidence ref checks, secret scan, and optional `promtool`/`kubeconform` checks.
121-
- Evidence bundle export with README, checksums, and a machine-readable manifest.
121+
- Evidence bundle export with README, checksums, and a schema-backed machine-readable manifest.
122122
- Evidence bundle path-safety checks that reject symlinks in exported project sections.
123123
- No-op assist provider and explicit opt-in LiteLLM assist.
124124
- GitHub Enterprise to Forgejo assessment path with fixture import and live repository inventory collection.

docs/cli.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ The checked-in Datadog example can be refreshed with `make example VERSION=0.1.0
2929

3030
`openexit validate` performs typed consistency checks, embedded JSON Schema validation, Grafana dashboard candidate validation, Prometheus alert-rule candidate validation, OpenTelemetry collector candidate validation, ArgoCD candidate validation, Forgejo migration candidate validation, identity realm/client candidate validation, edge VCL/HAProxy/Coraza candidate validation, LiteLLM/vLLM candidate validation, YAML/JSON parse checks, evidence reference checks, secret scanning, and optional external tool checks when `promtool` or `kubeconform` are installed.
3131

32-
`openexit export` refuses to package symlinks from exported project sections, even with `--force`, so evidence bundles cannot accidentally include files from outside the project tree. Exported zips include `openexit-evidence/manifest.json` with build metadata, project source/target, validation totals, and per-file SHA-256 digests, plus `checksums.txt` for archive-level verification.
32+
`openexit export` refuses to package symlinks from exported project sections, even with `--force`, so evidence bundles cannot accidentally include files from outside the project tree. Exported zips include `openexit-evidence/manifest.json` with build metadata, project source/target, validation totals, and per-file SHA-256 digests, plus `checksums.txt` for archive-level verification. The manifest shape is published as `schemas/openexit.evidence-bundle.schema.json`.
3333

3434
Generate individual artifacts with `openexit generate --artifact <name>`. The primary Datadog path supports `mapping`, `assessment`, `risk-register`, `manual-review`, `cost-drivers`, `target-architecture`, `acceptance-criteria`, `rollback-plan`, `runbook`, `restore-drill-checklist`, `alert-shadowing-plan`, `migration-plan`, `grafana-dashboards`, `prometheus-rules`, `opentelemetry`, and `argocd`. The GitHub Enterprise path also supports `forgejo-migration-candidate`; the identity path also supports `realm-client-candidate`; the edge path also supports `vcl-candidates`, `haproxy-candidates`, and `coraza-rule-candidates`; the AI provider path also supports `litellm-config-candidate`.
3535

docs/release.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Release-blocking v0.1 requirements:
1717
- Grafana, Prometheus, OpenTelemetry, and ArgoCD candidate generators.
1818
- Typed migration plan manifest with assessment, pilot, shadow, and cutover phase gates.
1919
- Validation engine with embedded JSON Schema checks, Grafana dashboard, Prometheus alert, OpenTelemetry collector, ArgoCD, Forgejo migration, identity realm/client, edge VCL/HAProxy/Coraza, and LiteLLM/vLLM candidate checks, YAML/JSON parsing, evidence refs, secret scan, optional promtool, and optional kubeconform.
20-
- Evidence bundle export with checksums, a machine-readable manifest, and OpenExit version metadata.
20+
- Evidence bundle export with checksums, a schema-backed machine-readable manifest, and OpenExit version metadata.
2121
- No-op AI assist and optional external assist behind explicit opt-in.
2222
- Documentation, examples, CI, release draft workflow, and reproducible release artifacts.
2323

@@ -60,7 +60,7 @@ The AI provider path is complete for local fixture assessment workflows and incl
6060
- [ ] `validation/validation-report.md` includes `litellm-config-candidate: passed` for the OpenAI/Anthropic to vLLM/LiteLLM pipeline.
6161
- [ ] `CHANGELOG.md` has a `0.1.0` section.
6262
- [ ] Exported bundle README includes version, commit, build date, bundle timestamp, and candidate warning.
63-
- [ ] Exported bundle `manifest.json` includes build metadata, project source/target, validation totals, and per-file SHA-256 digests.
63+
- [ ] Exported bundle `manifest.json` includes build metadata, project source/target, validation totals, and per-file SHA-256 digests, and validates against `schemas/openexit.evidence-bundle.schema.json`.
6464
- [ ] Export refuses symlinks in exported project sections, including when `--force` is used.
6565
- [ ] No credentials, tokens, passwords, or private keys are present in fixtures, generated files, docs, or bundles.
6666
- [ ] Draft release notes have been reviewed.

docs/schemas.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
OpenExit schemas live under `schemas/` and mirror the typed Go manifests. Release binaries embed these public Draft 7 JSON Schemas, and `openexit validate` checks project, inventory, assessment, mapping, migration-plan, and validation-report manifests against the embedded copies in addition to typed consistency checks and YAML/JSON parse checks.
44

5+
Evidence bundle exports include `openexit-evidence/manifest.json`, which follows `schemas/openexit.evidence-bundle.schema.json`. The manifest records OpenExit build metadata, project source/target metadata, validation totals, and SHA-256 digests for exported project files so downstream review tooling can verify a bundle without parsing every human-readable report first.
6+
57
Project manifests must use one of the supported source/target pairs: Datadog to Grafana LGTM, GitHub Enterprise to Forgejo, Okta/Auth0 to Keycloak/Zitadel, Cloudflare/Akamai to Varnish/HAProxy/Coraza, or OpenAI/Anthropic to vLLM/LiteLLM.
68

79
Inventory dashboards can include optional `dataSources` and `templateVariables` fields so assessment can flag Grafana mapping risk. Datadog fixture and live collectors populate `metrics` from captured dashboard and monitor queries, including referenced tag keys where available. The live Datadog collector also populates `integrations` from the Datadog v2 Integrations API when accessible. SLOs can include optional `sli`, `burnRateMonitorIds`, and `dashboardRefs` fields. The top-level inventory `volumes` section records whether log and trace volume assumptions are known.

internal/app/e2e_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ import (
1111
"path/filepath"
1212
"strings"
1313
"testing"
14+
15+
publicschemas "github.com/RamazanKara/openexit/schemas"
16+
"github.com/santhosh-tekuri/jsonschema/v6"
1417
)
1518

1619
func TestDefinitionOfDonePipelineAndBundle(t *testing.T) {
@@ -1208,6 +1211,9 @@ func verifyBundleManifest(files map[string][]byte) error {
12081211
if err := json.Unmarshal(data, &manifest); err != nil {
12091212
return err
12101213
}
1214+
if err := validateBundleManifestSchema(data); err != nil {
1215+
return err
1216+
}
12111217
if manifest.APIVersion != "openexit.dev/v1alpha1" || manifest.Kind != "EvidenceBundle" {
12121218
return &checksumError{message: "unexpected bundle manifest identity"}
12131219
}
@@ -1240,6 +1246,31 @@ func verifyBundleManifest(files map[string][]byte) error {
12401246
return nil
12411247
}
12421248

1249+
func validateBundleManifestSchema(data []byte) error {
1250+
schemaData, err := publicschemas.FS.ReadFile("openexit.evidence-bundle.schema.json")
1251+
if err != nil {
1252+
return err
1253+
}
1254+
compiler := jsonschema.NewCompiler()
1255+
compiler.DefaultDraft(jsonschema.Draft7)
1256+
document, err := jsonschema.UnmarshalJSON(bytes.NewReader(schemaData))
1257+
if err != nil {
1258+
return err
1259+
}
1260+
if err := compiler.AddResource("openexit.evidence-bundle.schema.json", document); err != nil {
1261+
return err
1262+
}
1263+
schema, err := compiler.Compile("openexit.evidence-bundle.schema.json")
1264+
if err != nil {
1265+
return err
1266+
}
1267+
instance, err := jsonschema.UnmarshalJSON(bytes.NewReader(data))
1268+
if err != nil {
1269+
return err
1270+
}
1271+
return schema.Validate(instance)
1272+
}
1273+
12431274
func verifyChecksums(files map[string][]byte) error {
12441275
checksumData, ok := files["openexit-evidence/checksums.txt"]
12451276
if !ok {
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"$id": "https://openexit.dev/schemas/openexit.evidence-bundle.schema.json",
4+
"title": "OpenExit Evidence Bundle Manifest",
5+
"type": "object",
6+
"required": ["apiVersion", "kind", "generatedAt", "build", "project", "validation", "files"],
7+
"additionalProperties": false,
8+
"properties": {
9+
"apiVersion": { "const": "openexit.dev/v1alpha1" },
10+
"kind": { "const": "EvidenceBundle" },
11+
"generatedAt": { "type": "string", "format": "date-time" },
12+
"build": {
13+
"type": "object",
14+
"required": ["version", "commit", "date"],
15+
"additionalProperties": false,
16+
"properties": {
17+
"version": { "type": "string", "minLength": 1 },
18+
"commit": { "type": "string", "minLength": 1 },
19+
"date": { "type": "string", "minLength": 1 }
20+
}
21+
},
22+
"project": {
23+
"type": "object",
24+
"required": ["name", "source", "target"],
25+
"additionalProperties": false,
26+
"properties": {
27+
"name": { "type": "string", "minLength": 1 },
28+
"source": { "type": "string" },
29+
"target": { "type": "string" }
30+
}
31+
},
32+
"validation": {
33+
"type": "object",
34+
"required": ["status", "checks", "passed", "failed", "warnings"],
35+
"additionalProperties": false,
36+
"properties": {
37+
"status": { "enum": ["passed", "failed"] },
38+
"checks": { "type": "integer", "minimum": 0 },
39+
"passed": { "type": "integer", "minimum": 0 },
40+
"failed": { "type": "integer", "minimum": 0 },
41+
"warnings": { "type": "integer", "minimum": 0 }
42+
}
43+
},
44+
"files": {
45+
"type": "array",
46+
"minItems": 1,
47+
"items": {
48+
"type": "object",
49+
"required": ["path", "size", "sha256"],
50+
"additionalProperties": false,
51+
"properties": {
52+
"path": {
53+
"type": "string",
54+
"minLength": 1,
55+
"pattern": "^[^/].*"
56+
},
57+
"size": { "type": "integer", "minimum": 0 },
58+
"sha256": { "type": "string", "pattern": "^[a-f0-9]{64}$" }
59+
}
60+
}
61+
}
62+
}
63+
}

0 commit comments

Comments
 (0)