Define the data-classification taxonomy and the per-wing / per-repo tagging mechanism the capture server reads to enforce I1. Pure prerequisite — no code dependency on the other sub-issues; can start immediately.
Per spec decision §4.1 (model C), the server must independently derive a target's classification to cross-check the caller's declared value (stricter wins). That derivation needs a tag to read. This issue provides it.
What to build
Taxonomy — the ordered sensitivity levels. Proposed: public < internal < confidential (ordered so "stricter wins" is a simple max). Confirm/adjust against the sovereignty model in infra/docs/architecture/01-invariants.md (I1) and the trust zones (sovereign-soil / external-metered / us-nexus) — the classification must map cleanly onto placement rules.
Per-wing tag (brain) — a way to tag a brain wing's classification (e.g. frontmatter on a wing _index.md, or a wing-classification.yaml the server reads). A client-* wing defaults to confidential; hyperguild/homelab default to internal; explicitly-public wings to public.
Per-repo tag (gitea) — classification for a Gitea repo target (repo topic, a .classification file, or a server-side map). Same defaulting logic.
Derivation function — deriveClassification(target) -> level the use-case calls. Unknown/untagged target fails safe to the strictest (confidential), never to permissive.
Acceptance criteria
Taxonomy levels defined + ordered; documented (brain note + referenced from the spec)
Per-wing classification tag mechanism implemented and readable by the server
Per-repo classification tag mechanism implemented and readable by the server
deriveClassification returns the level for a tagged target; untagged → confidential (fail safe)
This is the load-bearing prerequisite: until it exists, the I1 sovereignty gate (#49d) and the Q4 classification-aware audit posture (#49f) are not enforceable. Fail-safe-to-strictest is the critical correctness property — a missing tag must never silently downgrade.
**Parent:** #49 · **Spec:** `specs/capture-bdd-spec.md` §4.1 · **Blocks the I1 gate in:** #49d, #49f
## Scope
Define the data-classification taxonomy and the per-wing / per-repo tagging mechanism the capture server reads to enforce I1. Pure prerequisite — no code dependency on the other sub-issues; can start immediately.
Per spec decision §4.1 (model C), the server must independently derive a target's classification to cross-check the caller's declared value (stricter wins). That derivation needs a tag to read. This issue provides it.
## What to build
1. **Taxonomy** — the ordered sensitivity levels. Proposed: `public` < `internal` < `confidential` (ordered so "stricter wins" is a simple max). Confirm/adjust against the sovereignty model in `infra/docs/architecture/01-invariants.md` (I1) and the trust zones (sovereign-soil / external-metered / us-nexus) — the classification must map cleanly onto placement rules.
2. **Per-wing tag (brain)** — a way to tag a brain wing's classification (e.g. frontmatter on a wing `_index.md`, or a `wing-classification.yaml` the server reads). A `client-*` wing defaults to `confidential`; `hyperguild`/`homelab` default to `internal`; explicitly-public wings to `public`.
3. **Per-repo tag (gitea)** — classification for a Gitea repo target (repo topic, a `.classification` file, or a server-side map). Same defaulting logic.
4. **Derivation function** — `deriveClassification(target) -> level` the use-case calls. Unknown/untagged target **fails safe to the strictest** (`confidential`), never to permissive.
## Acceptance criteria
- [ ] Taxonomy levels defined + ordered; documented (brain note + referenced from the spec)
- [ ] Per-wing classification tag mechanism implemented and readable by the server
- [ ] Per-repo classification tag mechanism implemented and readable by the server
- [ ] `deriveClassification` returns the level for a tagged target; **untagged → confidential (fail safe)**
- [ ] Defaulting rules applied (`client-*` → confidential; homelab/hyperguild → internal)
- [ ] Unit tests incl. the untagged-fails-safe path
- [ ] `task check` green
## Notes
This is the load-bearing prerequisite: until it exists, the I1 sovereignty gate (#49d) and the Q4 classification-aware audit posture (#49f) are not enforceable. Fail-safe-to-strictest is the critical correctness property — a missing tag must never silently downgrade.
New package ingestion/internal/classification/. Taxonomy public < internal < confidential (ordered → "stricter wins" = max). Config.Derive(Target) / Wing / Repo resolve a destination's level: explicit tag (from optional classification.yaml) wins → client-*=confidential → hyperguild/homelab=internal → everything else=confidential (fail safe).
Design call: central classification.yaml at the brain root, not_index.md frontmatter (BuildWingIndex regenerates it) and not gitea repo topics (would couple #50 to the #52 tracker — chose the server-readable map so #50 has zero dependency on #52, keeping both epic roots parallel-buildable). Auditable in one place (I2/I5).
All ACs met incl. untagged-fails-safe ("" and unknown wings/repos → confidential), stricter-wins, explicit-override, bad-level-in-file → hard error, case-insensitive matching. task check green.
Unblocks: the I1 gate in #53 and the audit posture in #54 now have a classification source to read. Next on the critical path: #52 (gitea tracker, depends on #51) — and #51 (CaptureService + BrainStore extraction) is the other parallel root, ready to start.
## Implemented — PR #56 (open, not merged)
**PR:** https://git.d-ma.be/mathias/hyperguild/pulls/56
New package `ingestion/internal/classification/`. Taxonomy `public < internal < confidential` (ordered → "stricter wins" = `max`). `Config.Derive(Target)` / `Wing` / `Repo` resolve a destination's level: explicit tag (from optional `classification.yaml`) wins → `client-*`=confidential → `hyperguild`/`homelab`=internal → **everything else=confidential (fail safe)**.
**Design call:** central `classification.yaml` at the brain root, **not** `_index.md` frontmatter (BuildWingIndex regenerates it) and **not** gitea repo topics (would couple #50 to the #52 tracker — chose the server-readable map so #50 has zero dependency on #52, keeping both epic roots parallel-buildable). Auditable in one place (I2/I5).
All ACs met incl. untagged-fails-safe (`""` and unknown wings/repos → confidential), stricter-wins, explicit-override, bad-level-in-file → hard error, case-insensitive matching. `task check` green.
Docs: spec §4.1 "Implemented (#50)" note + brain `wiki/hyperguild/decisions/capture-classification-taxonomy`.
**Unblocks:** the I1 gate in #53 and the audit posture in #54 now have a classification source to read. Next on the critical path: #52 (gitea tracker, depends on #51) — and #51 (CaptureService + BrainStore extraction) is the other parallel root, ready to start.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Parent: #49 · Spec:
specs/capture-bdd-spec.md§4.1 · Blocks the I1 gate in: #49d, #49fScope
Define the data-classification taxonomy and the per-wing / per-repo tagging mechanism the capture server reads to enforce I1. Pure prerequisite — no code dependency on the other sub-issues; can start immediately.
Per spec decision §4.1 (model C), the server must independently derive a target's classification to cross-check the caller's declared value (stricter wins). That derivation needs a tag to read. This issue provides it.
What to build
public<internal<confidential(ordered so "stricter wins" is a simple max). Confirm/adjust against the sovereignty model ininfra/docs/architecture/01-invariants.md(I1) and the trust zones (sovereign-soil / external-metered / us-nexus) — the classification must map cleanly onto placement rules._index.md, or awing-classification.yamlthe server reads). Aclient-*wing defaults toconfidential;hyperguild/homelabdefault tointernal; explicitly-public wings topublic..classificationfile, or a server-side map). Same defaulting logic.deriveClassification(target) -> levelthe use-case calls. Unknown/untagged target fails safe to the strictest (confidential), never to permissive.Acceptance criteria
deriveClassificationreturns the level for a tagged target; untagged → confidential (fail safe)client-*→ confidential; homelab/hyperguild → internal)task checkgreenNotes
This is the load-bearing prerequisite: until it exists, the I1 sovereignty gate (#49d) and the Q4 classification-aware audit posture (#49f) are not enforceable. Fail-safe-to-strictest is the critical correctness property — a missing tag must never silently downgrade.
Implemented — PR #56 (open, not merged)
PR: #56
New package
ingestion/internal/classification/. Taxonomypublic < internal < confidential(ordered → "stricter wins" =max).Config.Derive(Target)/Wing/Reporesolve a destination's level: explicit tag (from optionalclassification.yaml) wins →client-*=confidential →hyperguild/homelab=internal → everything else=confidential (fail safe).Design call: central
classification.yamlat the brain root, not_index.mdfrontmatter (BuildWingIndex regenerates it) and not gitea repo topics (would couple #50 to the #52 tracker — chose the server-readable map so #50 has zero dependency on #52, keeping both epic roots parallel-buildable). Auditable in one place (I2/I5).All ACs met incl. untagged-fails-safe (
""and unknown wings/repos → confidential), stricter-wins, explicit-override, bad-level-in-file → hard error, case-insensitive matching.task checkgreen.Docs: spec §4.1 "Implemented (#50)" note + brain
wiki/hyperguild/decisions/capture-classification-taxonomy.Unblocks: the I1 gate in #53 and the audit posture in #54 now have a classification source to read. Next on the critical path: #52 (gitea tracker, depends on #51) — and #51 (CaptureService + BrainStore extraction) is the other parallel root, ready to start.