Files
mdm-ordinis/docs/user-guide/bundle-authoring.md
T
Zimin A.N. 8adfd39bff feat(bundle): bundle hygiene v2 — manifest spec + signing verifier + docs
Ships marketplace-design-v2 scoped to bundle hygiene (NO consumer UI, NO
install dialog, NO marketplace). 2-3 day cap respected — Approach C narrow
slice. Anti-feature commitment: scope creep → new design doc + /office-hours.

## What ships

### New module ordinis-bundle-spec
- BundleManifest.java — canonical apiVersion: ordinis.io/v1 record
  (Metadata + DictionaryEntry, Jackson-bindable, forward-compat ignoreUnknown)
- ManifestParser.java — YAML → record + structural validate
  (apiVersion/kind enforce, semver regex, bundle-id regex, path traversal
  guard на schemaRef, unique dict names, scope enum)
- BundleSignatureVerifier.java — ed25519 sign/verify (JDK 21 native)
- Zero Spring/JPA/Kafka deps — pure Java + Jackson YAML

### CUOD reference implementation
- ordinis-cuod-bundle/manifest.yaml — 40 dicts canonical entries
- Test fixture pin: cuod-manifest-fixture.yaml parses+validates clean
  (regression guard на каждом CI run)

### Tests
- 12 cases ManifestParser (happy + reject paths)
- 7 cases BundleSignatureVerifier (sign/verify roundtrip, tamper, wrong key)
- 1 case CUOD fixture pin
- Total 20 tests, all green

### Docs
- docs/integration/bundle-format-spec.md — formal spec для Java handoff
- docs/user-guide/bundle-authoring.md — how-to для новых bundle authors

## Why now (N=1 customer)

Per office-hours v2 decision: marketplace UI build = N customers value scale,
but bundle hygiene + sign format = AT N=1 ROI:
- Java team handoff requires documented spec независимо
- Sales demo «here's how customer #2 would ship custom bundle»
- Optionality для marketplace UI когда customer #2 LOI surfaces

## Explicitly NOT shipped (Phase 2 scope if ever)

- Publisher Maven plugin (separate artifact when actual publish needed)
- Browse catalog UI / install dialog (no customer #2 with use case)
- Dep resolution / topological install
- Multi-tenant Nexus namespacing
- Bundle uninstall flow
2026-05-17 10:39:07 +03:00

4.5 KiB

Authoring a Bundle

How to package справочники как distributable Ordinis bundle.

См. также: Bundle Format Specification — formal spec.


Quick start

# 1. Create bundle structure
mkdir -p my-bundle/src/main/resources/bundles/my-id
cd my-bundle

# 2. Add at least one schema
cat > src/main/resources/bundles/my-id/widget.schema.json <<'JSON'
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "x-id-source": "code",
  "required": ["code", "name"],
  "additionalProperties": false,
  "properties": {
    "code": { "type": "string", "pattern": "^[A-Z][A-Z0-9_]{1,31}$", "x-unique": true },
    "name": {
      "type": "object", "x-localized": true,
      "patternProperties": { "^[a-z]{2}-[A-Z]{2}$": { "type": "string" } },
      "required": ["ru-RU"]
    }
  }
}
JSON

# 3. Write manifest
cat > manifest.yaml <<'YAML'
apiVersion: ordinis.io/v1
kind: Bundle
metadata:
  id: my-org.widgets
  name: My Widgets Bundle
  version: 1.0.0
  scope: PUBLIC
dictionaries:
  - name: widget
    schemaVersion: 1.0.0
    scope: PUBLIC
    schemaRef: src/main/resources/bundles/my-id/widget.schema.json
YAML

Versioning

  • metadata.version — semver. Patch на data fixes, minor на add'инге dict'ов, major на breaking schema changes.
  • dictionaries[].schemaVersion — per-dict semver, independent от bundle version. Bump когда меняется shape конкретной schema.
  • Breaking schema change (remove required field, change type, add additionalProperties: false) → MAJOR bump bundle.

Validation locally

# Через maven (если bundle — Maven artifact):
mvn test -pl ordinis-bundle-spec -Dtest=CuodManifestFixtureTest

# Или programmatically:
import cloud.nstart.terravault.ordinis.bundle.spec.*;

byte[] yaml = Files.readAllBytes(Path.of("manifest.yaml"));
List<String> errors = ManifestParser.parseAndValidate(yaml);
if (!errors.isEmpty()) {
  errors.forEach(System.err::println);
  System.exit(1);
}

Signing (production publish)

Ed25519 keypair generation:

openssl genpkey -algorithm Ed25519 -out signing-priv.pem
openssl pkey -in signing-priv.pem -pubout -outform DER -out signing-pub.der

Sign bundle archive (until publisher Maven plugin lands — manual для now):

byte[] archive = Files.readAllBytes(Path.of("bundle.tar.gz"));
byte[] privKey = Files.readAllBytes(Path.of("signing-priv.der")); // convert PEM→DER first
String sig = BundleSignatureVerifier.sign(archive, privKey);
// → patch manifest.yaml: metadata.signature: <sig>
//   OR write sidecar bundle.sig: <sig>

Public key распространяется к operators которые verify bundles перед install.


Conventions

Naming

  • metadata.id — reverse-DNS (ru.cuod.dzz-ground-segment, com.acme.widgets).
  • dictionaries[].name — snake_case (spacecraft, ground_station, frequency_band).

Locales

Localized fields (x-localized: true) — каждое значение это объект {lang-COUNTRY: "..."}:

"name": { "ru-RU": "Космический аппарат", "en-US": "Spacecraft" }

Bundle сам не объявляет supported locales — это per-dict. Manifest may add optional metadata.locales в future v1.x но spec строго ignores unknown сейчас.

Foreign keys

x-references: "<target_dict>.<field>" — backend validates на write. Если target dict в том же bundle — order ensured (см. consumer protocol §6 в spec).


Anti-patterns

  • Не shipпите additionalProperties: true в production schemas — типизация теряется
  • Не используйте $ref к внешним URLs (security risk + offline build fail)
  • Не делайте 100+ dicts в одном bundle — расщепляйте по domain (e.g. cuod-spacecraft, cuod-radio)
  • Не bump'айте schemaVersion без реального schema change

Handoff checklist (Java team)

Когда передаёте bundle authoring к новой команде:

  1. Прочитать bundle-format-spec.md
  2. Изучить ordinis-cuod-bundle/manifest.yaml как working example
  3. Прогнать mvn test -pl ordinis-bundle-spec локально
  4. Generate signing keypair, distribute pub key к ops
  5. Set up CI publish job (когда ordinis-bundle-publisher plugin landed)

Для вопросов: ordinis maintainers (см. CODEOWNERS).