Asset & Information Classification Procedure
Formalises the classification scheme already defined and applied in register/assets.yaml's header comment into a repeatable procedure — how a new asset gets classified when it's created, and how the existing 13-asset inventory stays current rather than going stale the way dsgvo-data-flow.md's processor table did.
1. Classification scheme
Four levels, already in use across assets.yaml:
| Level | Meaning |
|---|---|
| Public | Intended for open distribution; no confidentiality expectation |
| Internal | Not published, but low sensitivity if disclosed internally |
| Confidential | Personal data or business-sensitive; disclosure would cause real harm |
| Restricted | Highest sensitivity — auth material, audit trail, or privileged infrastructure access |
2. When this procedure triggers
A new asset needs classification whenever one of the following happens:
- A new database table is added via a Supabase migration (
supabase/migrations/00NN_*.sql). - A new service integration goes live (a new third-party vendor, a new internal service).
- A new document is created that would itself qualify as an information asset (an operational
record, a register, a document containing personal or business-sensitive content).
3. Who decides
- ISMS Owner (Toby) classifies infrastructure and code assets — database tables, services, the
production host, backups, secrets storage, the source repository itself. This matches his existing ownership of all 12 non-document entries in assets.yaml today.
- DPO / Privacy Owner (Antonios) classifies anything privacy-relevant that isn't primarily an
infrastructure question — a new document like a RoPA entry, a new category of subscriber-facing disclosure, or any asset whose classification hinges on a legal-basis or data-subject-rights judgment rather than a technical one. ASSET-013 (dsgvo-data-flow.md itself) is already owned by Antonios on this basis.
- Where an asset is ambiguous (e.g. a new table that is both a technical addition and privacy-bearing
by design), the ISMS Owner proposes a classification and the DPO confirms or corrects it before the entry is finalised — consistent with the Consulted/Accountable split already used for supplier review in governance/GOV-03-roles-and-raci.md.
4. Classification steps
- Identify the trigger (§2).
- Apply the classification test, in order:
- Does it contain, or provide access to, authentication material, the audit trail, or privileged
infrastructure access? → Restricted.
- Does it contain personal data or business/client-confidential information, where disclosure
would cause real harm? → Confidential.
- Is it not published externally, but low-sensitivity if seen internally? → Internal.
- Is it intended for open, public distribution? → Public.
- Add the entry to
register/assets.yamlwith all fields populated:id,name,type,
classification, owner, location, contains_pii, contains_client_confidential, retention. An entry with a classification but no retention/owner is incomplete — do not leave fields as placeholders.
- Cross-reference, if the asset carries personal data, with
dsgvo-data-flow.md§2 so the
retention column there and the retention field in assets.yaml don't diverge.
5. Worked examples, from the real inventory
- `public.audit_log` → restricted (`ASSET-003`). RLS is enabled with no SELECT policy at all —
the only read path is the service-role key. It contains PII (actor/subject identifiers) and is the system's compliance-critical tamper-evidence record: unauthorized disclosure or successful tampering would undermine every audit-trail claim this ISMS makes, not just leak one subscriber's data. That combination — structural lockdown plus outsized consequence if compromised — is what restricted is for.
- The stratify source code repository → internal (`ASSET-008`). It contains no PII and no
client-confidential data directly, so it doesn't rise to confidential/restricted on the harm test. But it is not intended for open distribution either — it holds business logic, infrastructure configuration shape, and competitive detail that shouldn't be public. Internal is the correct middle tier: sensitive enough to keep private, not sensitive enough to warrant the access controls reserved for restricted assets.
- `public.subscribers` → confidential (`ASSET-001`). Real PII (email, name, subscription state)
with disclosure consequences that matter — a genuine GDPR-relevant breach if exposed. It does not reach restricted because it is not itself an auth-material or audit-integrity asset; RLS on subscribers follows the ordinary auth.uid() = user_id self-access pattern rather than the deny-by-default posture reserved for audit_log and signal_deliveries.
- The production host → restricted (`ASSET-009`). Both
contains_piiand
contains_client_confidential are true, and it is privileged infrastructure access by definition — compromise of the host compromises every other asset transitively. This is the clearest case for restricted: it isn't data itself, it's the thing everything else's confidentiality depends on.
6. Review cadence for the existing inventory
- Per-migration: whoever writes a new
supabase/migrations/00NN_*.sqlthat adds a table should
add or update the corresponding assets.yaml entry in the same pull request. This is not currently enforced by CI — stated as a gap, not a control — but is the expected practice going forward.
- Annual full pass: once a year, walk all entries in
register/assets.yamland confirm each
one's classification, owner, and retention are still accurate — particularly after any infrastructure change (a hosting migration is exactly the kind of event that can silently invalidate a location field, as it did across dsgvo-data-flow.md and pentest-rfp.md after the Hetzner cutover).
7. Evidence this procedure produces
register/assets.yaml itself, kept current — version-controlled, so every classification change has an author, timestamp, and diff (register/schema.md's "why YAML + git" rationale applies directly here).
8. Review
This procedure is reviewed annually, alongside the full inventory pass in §6.