From 35cf3f405f7a8833aa9613e9b472a84ebe721336 Mon Sep 17 00:00:00 2001 From: John Coffey Date: Mon, 28 Sep 2026 01:36:57 -0700 Subject: [PATCH 1/6] Spec: personal-data catalog, compliance role, Overview and Data inventory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 1 of the GDPR auditor foundation: the investigation and the design, committed before anything is built (SPEC.md §3 rule 3). It maps every place the server stores or sends personal data found in the code at de275ba, each with its categories, whose data it is, the settings that control it, what bounds its retention, where it lives, whether it leaves the host, its scope and the code that writes it, and the default in a new install. It proposes a sidecar catalog (resources/privacy/catalog.toml), since the schema and registry code are upstream's generated output with no generator here; a CI check modeled on name-check.py; a strip-report section; a read-only inventory method with dated snapshots; a Compliance Officer role; and the Compliance navigation with Overview and Data inventory. Findings worth reading on their own: webhooks ignore levels and, at their defaults, receive every event including raw SMTP input; log files are never deleted; automatic bans never expire; some of the fork's records outlive the account; spam training keeps whole messages for 180 days; traces are on in a new install; the spam filter sends IPs, domains, hashed addresses and body digests to third-party services by default. Proposed default changes (new installs only) and seven open questions are for John to decide. No default is changed. --- docs/spec/features/personal-data-catalog.md | 627 ++++++++++++++++++++ 1 file changed, 627 insertions(+) create mode 100644 docs/spec/features/personal-data-catalog.md diff --git a/docs/spec/features/personal-data-catalog.md b/docs/spec/features/personal-data-catalog.md new file mode 100644 index 0000000..e7ea6a8 --- /dev/null +++ b/docs/spec/features/personal-data-catalog.md @@ -0,0 +1,627 @@ +# Feature spec: personal-data catalog, compliance role, and the Compliance section's first pages + +Status: draft, 2026-09-28. Phase 1 of the GDPR auditor foundation: the +investigation and the design, for approval before anything is built. Not a +rebuild of an upstream feature, so it has no line in SPEC.md §4's table. + +## Provenance + +Written for the record SPEC.md §3 rule 3 asks for. Sources, and nothing else: + +| Source | License | Used for | +|---|---|---| +| This repository at `de275ba` (2026-09-28): the code, `resources/schema/schema.json.gz`, `tools/fork/`, `.gitea/workflows/` | AGPL-3.0-only | Every claim in the source map: each row names the code that writes the data | +| inbuxa-admin (the console) at `8e281e5` | AGPL-3.0-only | How navigation, hand-built pages and visibility work | +| docs.inbuxa.org (the inbuxa.org repository at `6b12467`) | Own | What the documentation says, for "Contradictions" | +| `inbuxa-drafts/specs/audit-hold-lock.md`, `legacy-protocols.md` | Own | The Compliance pages that already exist, and the permission-rollout notes | +| Regulation (EU) 2016/679 (GDPR), Articles 5, 25, 30 and 32 | Public law | The shape of a record of processing: categories, whose data, where, how long, who receives it | + +No Enterprise-only file or snippet was used. Claims were checked by reading the +code; where a search found nothing, the row says so rather than asserting a +negative. + +## What it is + +Three things, the foundation for a GDPR auditor: + +1. **A personal-data catalog.** Every place the server can store or send + personal data, with its categories, whose data it is, where it lives, what + bounds its retention, and the settings that control it. What a particular + server holds depends on how its operator configured it, so the catalog + describes *possibilities*; the server evaluates it against live settings to + say what *this* server does (Phase 3). +2. **A compliance role** that can see and act on compliance data without being + able to change server settings. +3. **Two console pages** under Compliance: **Overview** and **Data + inventory**. + +The catalog records facts and the auditor reports findings. Neither judges: +nothing in code, docs, UI text or output claims the server meets a legal +standard, or that one is guaranteed. Nothing here changes a default; proposed +changes are listed under [Default profile](#default-profile) for John to +decide. + +**Out of scope** (their own specs): retention policies, data subject requests, +the guided questionnaire, jurisdiction packs, mobile and ihasmail screens. +Legal hold and the audit log are built already (audit-hold-lock spec, phases +1–4); this spec places them in the Compliance navigation and gives the +compliance role access to them, nothing more. + +## 1. Vocabulary + +**Categories** (fixed; a source may have several): + +| Category | Means | +|---|---| +| `identifier` | Names, email addresses, account and principal ids, login names | +| `contact` | Phone numbers, postal addresses, vCard details, contact addresses given for an object (an OAuth client's contacts) | +| `network` | IP addresses and ports, client hostnames (EHLO, PTR), user agents, device push URLs | +| `content` | Message bodies, subjects, attachments, events, contacts' cards, files, scripts, free-text descriptions and reasons | +| `metadata` | Timestamps, sizes, flags, envelopes, delivery status, message-ids, counts | +| `credential` | Passwords and their hashes, tokens, keys, secrets | + +**Whose** (data subjects): `holder` (the account holder), `correspondent` +(people who send to or receive from the server, and people in contact cards +and events), `administrator` (people acting on the server). + +**Where** (location): `data-store`, `blob-store`, `search-store`, +`in-memory-store` (the configured one; the data store by default), +`memory` (process RAM, per node), `log-file`, `external` (sent to an +endpoint). **Leaves the host** is a separate column: a store is local unless +the operator points it at a remote backend. + +**Scope**: `tenant` (attributable to a tenant through the account or domain), +`server` (server-wide only). + +## 2. Source map + +Defaults are those of a new install: the struct defaults, plus what first boot +inserts (`crates/common/src/manager/defaults.rs`). "Unbounded" means no setting +or process removes the data on a schedule. + +### 2.1 Mail, groupware and user content + +| Source | Categories | Whose | Controlled by | Retention bound | Where / leaves host | Scope | Written by | Default | +|---|---|---|---|---|---|---|---|---| +| Emails, mailboxes, threads | content, identifier, metadata | holder, correspondent | always on; `x:Email.encryptAtRest`, `x:Email.maxMessages` | unbounded until deleted; Trash emptied after `x:DataRetention.expungeTrashAfter` | data + blob store; leaves when sent | tenant | `email/src/message/ingest.rs` `email_ingest`; `delivery.rs` `deliver_message`; `message/delete.rs` `emails_delete`, `emails_auto_expunge` | collected; Trash 30 d | +| Email submissions | metadata (envelopes) | holder, correspondent | always on | `x:DataRetention.expungeSubmissionsAfter` | data store | tenant | `message/delete.rs` `purge_email_submissions` (purge) | 3 d | +| Full-text index: mail | content, identifier, metadata | holder, correspondent | `x:Search.indexEmail`, `indexEmailFields` | follows the item | search store (external if Elasticsearch/Meilisearch) | tenant | `services/src/task_manager/index.rs` `build_email_document` | indexed, all fields | +| Calendars, events, iTIP | content, identifier, metadata | holder, correspondent | `x:CalendarScheduling.enable`, `httpRsvpEnable`; `x:CalendarAlarm.enable` | unbounded; scheduling inbox `expungeSchedulingInboxAfter`, share notices `expungeShareNotifyAfter` | data + blob store; **iMIP mails event details to external attendees** | tenant | `dav/src/calendar/update.rs`; `jmap/src/calendar_event/set.rs`; `groupware/src/calendar/storage.rs`; `services/src/task_manager/imip.rs` | collected; 30 d / 30 d | +| Contacts, address books | contact, identifier | correspondent, holder | always on | unbounded | data store | tenant | `dav/src/card/update.rs`; `jmap/src/contact/set.rs`; `groupware/src/contact/storage.rs` | collected | +| Full-text index: calendars, contacts | content, contact, identifier | correspondent, holder | `x:Search.indexCalendar`/`Fields`, `indexContacts`/`Fields` | follows the item | search store | tenant | `index.rs` `build_calendar_document`, `build_contact_document` | indexed | +| Files (FileNode, WebDAV) | content, metadata | holder | `x:FileStorage.maxSize`, `maxFiles` | unbounded | data + blob store | tenant | `dav/src/file/update.rs`; `jmap/src/file/set.rs`; `groupware/src/file/storage.rs` | collected | +| Sieve scripts, vacation responses | content | holder (scripts may name correspondents) | user-set | unbounded | data + blob store | tenant | `jmap/src/sieve/set.rs`; `managesieve/src/op/putscript.rs`; `jmap/src/vacation/set.rs` | when set | +| Sieve "seen" ids (vacation, duplicate) | identifier (blake3 hash) | correspondent | used only by scripts that call vacation/duplicate | the script's TTL | in-memory store | tenant | `email/src/sieve/ingest.rs` | when used | +| Identities | identifier, content (signatures) | holder | `x:Email.maxIdentities` | unbounded | data store | tenant | `jmap/src/identity/set.rs` | collected | +| Push subscriptions | network (push URL), credential (keys); **EmailPush can carry email properties to the push URL** | holder | client opt-in; `x:Jmap.maxSubscriptions` | expire within 7 days | data store → **external push service** | tenant | `jmap/src/push/set.rs`; `services/src/state_manager/http.rs` | client-driven | + +### 2.2 Records kept by inbuxa's own features + +These live in inbuxa's own key space (`SUBSPACE_INBUXA`) unless noted. +`Store::danger_destroy_account` doesn't clear that space, so a row's +"survives account deletion" note matters. + +| Source | Categories | Whose | Controlled by | Retention bound | Where | Scope | Written by | Default | +|---|---|---|---|---|---|---|---|---| +| Archived items (undelete) | content, identifier, metadata | holder, correspondent | `x:DataRetention.archiveDeletedItemsFor`; a legal hold forces keeping | each item's `archivedUntil`; **held: year 9999** | registry (data store) + blob store | tenant | `features/src/undelete/records.rs` `insert`, `set_deadline`, `remove_expired`; `undelete/email.rs` `note`; `groupware/src/inbuxa.rs`; `services/.../index.rs` `archive_noted` | **off** | +| Deleted accounts kept | identifier (name, addresses), **credential (the whole account record, password hashes included)**, metadata | holder | `x:DataRetention.archiveDeletedAccountsFor`; a hold forces keeping | `kept_until`; held: year 9999 | data store | tenant | `jmap/src/inbuxa/deleted_account.rs` `keep`; `features/src/undelete/data.rs` `set_kept_account` | **off** | +| Legal holds | identifier, content (reference, description, reasons) | administrator, holder | `sysLegalHold*` | **unbounded, by design** | data store | server (scope may name tenants) | `features/src/hold/mod.rs` `create`, `update`, `pin_account`, `keep_moved` | none | +| Hold exports: records | identifier, content (reason), metadata | administrator | `sysLegalHoldExport` | **unbounded, by design** | data store | server | `hold/mod.rs` `create_export`, `update_export` | none | +| Hold exports: ZIP | content (everything the hold covers) | holder, correspondent | as above | `x:Jmap.uploadTtl`, then the next blob purge (`blobCleanupSchedule`) | blob store; **leaves when downloaded** | server | `jmap/src/inbuxa/hold_export.rs` `build` → `put_jmap_blob` | 1 h + up to a day | +| Account locks, delegations | identifier, content (reason), metadata | holder, administrator | `sysAccountLock*` | until the lock is removed; **no removal on account deletion found** | data store | tenant | `features/src/lock/mod.rs` `set`, `remove` | none | +| Audit log | identifier, **network (sign-in IPs)**, content (setting diffs, secrets redacted; reasons) | administrator, holder (as target), correspondent (e.g. banned IPs) | always on; `inbuxa:AuditSettings.keepForDays` (not a registry setting) | `keepForDays`, minimum 90; records about held accounts kept while held; **kept after account deletion** | data store (hash-chained) | tenant-filterable | `common/src/audit.rs` `audit_append`, `audit_sign_in`, `audit_sign_in_failed`, `audit_foreign_access`, `audit_delegate`; `jmap/src/inbuxa/audit.rs` `recorded`; purge `audit_purge` | **collected, 730 d** | +| Audit exports | as the audit log | as above | `sysAuditExport` | `x:Jmap.uploadTtl`, then the next blob purge | blob store; leaves when downloaded | server or tenant | `jmap/src/inbuxa/audit_log.rs` `build_export` | 1 h + up to a day | +| Masked addresses | identifier, content (description, site), metadata (last message) | holder | `x:Email.maxMaskedAddresses` | the registry object goes with the account; **the address tombstone is kept forever by design**; its `Mm`/`Mc` records aren't cleared on deletion (search found no clearing) | registry + data store | tenant | `features/src/masked_email/ops.rs`; `data.rs` `set_address`, `set_record`, `log_change` | when used (5 max) | +| Legacy-protocol last use | metadata (one timestamp per account and protocol; no IP) | holder | none | **unbounded; not cleared on account deletion** | data store | tenant | `features/src/security/legacy_use.rs` `record` (from `common/src/network/legacy.rs` `admit_legacy_session`) | **collected** | + +### 2.3 Mail transport and reports + +| Source | Categories | Whose | Controlled by | Retention bound | Where / leaves host | Scope | Written by | Default | +|---|---|---|---|---|---|---|---|---| +| Queue (`x:QueuedMessage`) | content, metadata, network (`receivedFromIp`) | holder, correspondent | always on | delivered, or `x:MtaDeliveryExpirationTtl.expire` | data + blob store; delivered out | server | `smtp/src/queue/spool.rs` `queue`, `save_changes`, `remove` | 3 d max | +| DSNs | content (**original headers**), metadata | correspondent, holder | `x:DsnReportSettings` | sent through the queue | **leaves** | server | `smtp/src/queue/dsn.rs` `send_dsn` | on | +| Received DMARC, TLS-RPT, ARF reports | network (source IPs), identifier (envelope/header from); ARF: **message and headers**, original recipient, user agent | correspondent | `x:ReportSettings.inboundReportAddresses`, `inboundReportForwarding` | `x:DataRetention.holdMtaReportsFor` | data store; also forwarded to the mailbox | tenant (by domain) | `smtp/src/reporting/analysis.rs` | collected, 30 d | +| DMARC aggregate reports we send | network (source IPs), identifier (domains) | correspondent | `x:DmarcReportSettings.aggregateSendFrequency`; only to domains publishing `rua` | kept until sent | data store → **external `rua`** | server | `smtp/src/reporting/dmarc.rs` | daily | +| DMARC/SPF/DKIM failure reports we send | content (**raw headers**), network, identifier | correspondent | `x:DmarcReportSettings.failureSendFrequency`, `x:SpfReportSettings`, `x:DkimReportSettings`; only when requested | through the queue | **external `ruf`** | server | `reporting/dmarc.rs` `send_dmarc_report`; `spf.rs`; `dkim.rs` | on, rate-limited | +| TLS-RPT we send | network (remote MX hosts) | operators of other servers | `x:TlsReportSettings.sendFrequency` | until sent (deletion path not confirmed) | **external** | server | `smtp/src/reporting/tls.rs` | daily | +| Relay (smart host) | everything in the message | holder, correspondent | `x:MtaRoute` (Relay) | receiver's | **external** | server | `smtp/src/outbound` | none | +| Milters, MTA hooks | network (client IP, PTR, HELO), identifier (SASL login), **full message** | holder, correspondent | `x:MtaMilter`, `x:MtaHook` | receiver's | **external** | server | `smtp/src/inbound/milter`, `inbound/hooks/mod.rs` | none | + +### 2.4 Telemetry and logs + +| Source | Categories | Whose | Controlled by | Retention bound | Where / leaves host | Scope | Written by | Default | +|---|---|---|---|---|---|---|---|---| +| Stored traces (`x:Trace`) | network (IP, EHLO), identifier (envelope addresses, account names), metadata (message-ids, results) | correspondent, holder | `x:TracingStore`; `x:EventTracingLevel` | `x:DataRetention.holdTracesFor` (unset = unbounded) | data store, or a remote PostgreSQL/MySQL/FDB | server (records name accounts) | `common/src/telemetry/tracers/store.rs` `spawn_store_tracer`; purge `purge_spans` | **collected** (first boot inserts `TracingStore::Default` although the struct default is off); 30 d | +| Trace search index | same keywords, lowercased | as above | `x:Search.indexTelemetry`, `indexTracingFields` | purged with the traces | search store | server | `index.rs` `trace_search_document` | indexed | +| Stored metrics | none personal (aggregates) | — | `x:MetricsStore`, `x:Metrics` | `holdMetricsFor` | data store | server | `telemetry/metrics/store.rs` | collected, 90 d | +| Log file | network (**client IP on every in-session line**, from the span), identifier (MAIL FROM, RCPT TO, account names), metadata (message-ids). No subject key exists; bodies only in trace-level raw-I/O events | correspondent, holder, administrator | `x:TracerLog` (`level`, `events`, `path`, `rotate`) | **unbounded: rotated daily, never deleted** | log file (`/var/log/inbuxa`); read back as `x:Log` and fed to Explain | server | `common/src/telemetry/tracers/log.rs` | **collected, info level** | +| Console / journald tracers | as the log file | as above | `x:Tracer` Stdout/Journal | the host's journal policy | stdout / journald (leaves only if the host forwards it) | server | `tracers/stdout.rs`, `journald.rs` | none (recovery mode adds a console tracer) | +| OpenTelemetry tracer | as the log file, every event and span key | as above | `x:Tracer` OtelHttp/OtelGrpc (`level`, `events`, `endpoint`) | receiver's | **external** | server | `tracers/otel.rs` | none | +| Webhooks | as the log file; **can include raw SMTP input (message content) and model replies** — see [Findings](#findings) 1 | as above | `x:WebHook` (`events`, `eventsPolicy`, `url`) | receiver's; retried up to `discardAfter` | **external** | server | `telemetry/webhooks/mod.rs` `post_webhook_events` | none | +| Alerts | contact (the addresses an administrator sets) | administrator | `x:Alert` | as mail | mail; may leave | server | `telemetry/alerts.rs` | none | +| Failed tasks | identifier (account names, e.g. a failed account destroy), content (iTIP messages) | holder, correspondent | none | **not confirmed: failed tasks are rescheduled at `u64::MAX` and no purge was found** | data store | server | `services/src/task_manager/manager.rs` | when a task fails | + +### 2.5 Sign-in and network defence + +| Source | Categories | Whose | Controlled by | Retention bound | Where | Scope | Written by | Default | +|---|---|---|---|---|---|---|---|---| +| Accounts, groups, lists, domains, tenants (registry) | identifier, contact, metadata | holder | registry | the object's life | data store | tenant | registry writes (`jmap/src/registry/`) | — | +| Passwords, app passwords, API keys | credential (argon2id hashes), network (`allowedIps`), metadata | holder | `x:Authentication.passwordHashAlgorithm`; `maxAppPasswords`, `maxApiKeys`; `expiresAt` | the account's life or `expiresAt` | data store | tenant | `jmap/src/registry/mapping/account.rs` | — | +| OAuth clients | identifier, contact (`contacts`), credential (secret hash) | administrator, third-party developer | `x:OidcProvider.requireClientRegistration` | `expiresAt` or unbounded | data store | tenant | `http/src/auth/oauth/registration.rs` | none | +| OAuth codes and tokens | credential | holder | `x:OidcProvider.*Expiry` | tokens are sealed and stateless (not stored); codes in the in-memory store with TTL | in-memory store | tenant | `http/src/auth/oauth/auth.rs`, `token.rs` | codes 10 min | +| Rate-limit state | network, identifier (login names) | holder, correspondent | `x:Http.rateLimit*`, `x:Imap.maxRequestRate`, `x:Security.*BanRate` | the rate's period | in-memory store | server | `common/src/auth/rate_limit.rs`, `network/security.rs` | on | +| Greylist | identifier (sender/recipient pairs, plain) | correspondent, holder | `x:SpamSettings.greylistFor` | that period | in-memory store | server | `smtp/src/inbound/rcpt.rs` | **off** | +| Automatic IP bans (`x:BlockedIp`) | network | correspondent, holder | `x:Security.authBanRate`, `abuseBanRate`, `loiterBanRate`, `scanBanRate` (on); `*BanPeriod` (**no default**) | **unbounded: a ban with no period never expires, and no purge of expired bans was found**; each ban is also an audit record | data store (registry) | server | `common/src/network/security.rs` `block_ip` | **collected, permanent** | +| Allowed IPs | network | administrator's choice | manual; `expiresAt` | optional | data store | server | registry | none | + +### 2.6 Spam filter and AI + +| Source | Categories | Whose | Controlled by | Retention bound | Where / leaves host | Scope | Written by | Default | +|---|---|---|---|---|---|---|---|---| +| Training samples (`x:SpamTrainingSample`) | content (**the whole message, pinned**), identifier (`from`), content (`subject`) | correspondent, holder | `x:SpamClassifier.model`, `learnSpamFromTraps`, `learnSpamFromRblHits`, `learnHamFromCard`, `learnHamFromReply`; users' Junk moves | `x:SpamClassifier.holdSamplesFor`; **outlives the user deleting the mail** | data + blob store | tenant for users' samples; **server, unattributed, for SMTP autolearn** | `email/src/message/ingest.rs` `add_spam_sample`; `smtp/src/queue/spool.rs` | **collected, 180 d** | +| Trainer state (`INBUXA_SPAM_TRAIN_DATA`) | metadata (blob hashes with account ids; hashed tokens that include addresses and domains) | correspondent, holder | as above | **unbounded (overwritten on training, never expired)** | blob store | server | `spam-filter/src/modules/classifier.rs` `spam_train` | after 100 + 100 samples | +| Trained model | hashed weights, not directly personal | — | as above | unbounded | blob store | server | same | same | +| AI spam classification | content (subject + up to `inbuxa:AiLimits.maxContentBytes` of text) | correspondent | `x:SpamLlm`, `x:AiModel` (`url`), `inbuxa:AiLimits` | the endpoint's; the verdict is written into the stored message | **external endpoint** (local or hosted; a remote URL only warns) | server | `spam-filter/src/analysis/llm.rs`; `common/src/enterprise/llm.rs` | **off** | +| Sieve `llm_prompt` | whatever a script sends | any | `x:AiModel`; `interactAi` | endpoint's | **external** | per account | `enterprise/llm.rs` `sieve_prompt` | no model | +| Explain | identifier (return path, recipients), network (trace IPs), metadata (remote replies, log details); never contents or raw I/O | correspondent, holder | `inbuxa:AiLimits.explainEnabled`, `explainModelId` | answer cache: memory, per node, 24 h, 1000 answers | **external endpoint** + memory | server | `jmap/src/inbuxa/explanation.rs`; `features/src/ai/explain/memory.rs` | no model | +| DNSBL lookups | network (client IPs), identifier (domains; **SHA-1 of addresses** to msbl.org), metadata (MD5 of URLs) | correspondent | bundled `x:SpamDnsblServer` rules; `x:SpamDnsblSettings` | receiver's | **external (DNS)** | server | spam-filter DNSBL module | **on: 17 of 18 bundled lists** | +| Pyzor | content (SHA-1 digest of normalized body) | correspondent | `x:SpamPyzor` | receiver's | **external: `public.pyzor.org`** | server | `spam-filter/src/modules/pyzor.rs` | **on** | +| URL-shortener expansion | network (fetches links, which can carry per-recipient tracking tokens) | correspondent, holder | bundled redirector list | — | **external (HTTP)** | server | `spam-filter/src/analysis/url.rs` | **on** | + +### 2.7 Stores and directories an operator can point elsewhere + +| Registry object | Receives | Default | +|---|---|---| +| `x:DataStore` | everything above that says data store | RocksDB, local | +| `x:BlobStore` (S3, Azure, FS, SQL, FDB) | message bodies, attachments, files, samples, exports | the data store | +| `x:SearchStore` (Elasticsearch, Meilisearch, SQL, FDB) | full text of mail, contacts, calendars; trace keywords | the data store | +| `x:InMemoryStore` (Redis) | rate limits, greylist, OAuth codes, Sieve seen ids | the data store | +| `x:TracingStore`, `x:MetricsStore` | traces, metrics | the data store | +| `x:Coordinator` (Kafka, NATS, Zenoh, Redis) | cluster broadcasts (whether personal: not confirmed) | off | +| `x:Directory` (LDAP, SQL, OIDC) | login names, passwords to verify, address lookups | internal | +| `x:AcmeProvider`, `x:DnsServer` | hostnames, a role address; no personal data found | Let's Encrypt if chosen; manual DNS | + +## Findings + +Facts from the investigation that matter beyond the catalog. Each is a finding, +not a judgment; what to do about each is John's call. + +1. **A webhook ignores levels and, left at its defaults, receives every + event, message content included.** `config/telemetry.rs` builds a webhook's + interests with `apply_events` alone: no level check. With the default + `eventsPolicy` (exclude) and no events listed, that is every event type, + including trace-level `smtp.raw-input` (the raw SMTP bytes, `DATA` + included) and `ai.llm-response`. The audit-log docs suggest a webhook to + pass records to a SIEM. Probably upstream behavior; not yet checked + against upstream. +2. **Log files are never deleted.** Daily rotation opens a new file; nothing + removes old ones, and no logrotate configuration ships. At the default + level every in-session line carries the client IP. +3. **Automatic IP bans are permanent.** No `*BanPeriod` has a default, and no + purge of expired `x:BlockedIp` records was found. +4. **Some records outlive the account.** Deleting an account doesn't clear + inbuxa's own key space: legacy-protocol last use, account locks, masked + address records and audit records stay (audit records by design). +5. **A kept deleted account keeps its password hashes** for + `archiveDeletedAccountsFor`, or indefinitely under a legal hold. +6. **Spam training keeps whole messages for 180 days**, after the user + deleted them, and SMTP autolearn keeps them with no account attribution, so + they can't be found by person. The trainer state never expires. +7. **Traces are on in a new install** although the struct default says off: + first boot inserts the tracing and metrics stores. +8. **Data leaves the host by default** through the spam filter (DNSBL queries + with IPs, domains and hashed addresses to 17 lists; Pyzor body digests; + shortener URL fetches) and through reports (DMARC aggregate daily; failure + reports with headers where requested). No AI, webhook, OpenTelemetry, + milter, hook or relay endpoint is configured by default. +9. **Export files outlive their stated lifetime by up to a day.** The link + expires with `uploadTtl`; the bytes stay until the next blob purge + (04:00 daily). +10. **Failed tasks may stay forever** (rescheduled at `u64::MAX`; no purge + found), holding account names or iTIP messages. Not confirmed by a test. + +## 3. How the schema is produced, and what the catalog works from + +The prompt says not to hand-edit generated files and to work from the source +of truth. What the investigation found: + +- **`resources/schema/schema.json.gz` and the registry Rust code + (`crates/registry/src/schema/*.rs`, headed "auto-generated") come from + upstream's generator, which isn't in this repository or upstream's public + tree.** Nothing here regenerates them. +- **The fork edits both by hand.** Since the v0.16.22 import the gz has + changed in 18 commits: the Compliance layout (Audit Log, Legal Holds, Locked + Accounts), the Local AI and Legacy Protocols pages, the fork's permissions + (`sysAudit*`, `sysAccountLock*`, `sysLegalHold*`, `sysAiExplain`), always + paired with `enums.rs`/`enums_impl.rs` edits marked `// inbuxa:`. Scripted + edits go through `tools/fork/renames.py` (`schema_bytes`), which writes the gz + with `gzip.compress(compresslevel=9, mtime=0)` and the `.sha256` sidecar as + unpadded URL-safe base64 of the gz's SHA-256. +- **`/api/schema`** (`crates/http/src/api/mod.rs`, `"schema"` arm) serves the + embedded gz as-is to any authenticated caller, unfiltered; the console + hides what a person can't use from the permission list `/api/account` + returns. +- **The schema carries 150 objects and 2,102 properties** (316 field + schemas). About 34 objects hold data about people, roughly 150–180 + properties counting nested structs; about 20 configuration objects carry + about 100 credential or contact properties. inbuxa's own objects + (`inbuxa:AuditEvent`, `LegalHold`, `HoldExport`, `AccountLock`, + `DeletedAccount`, `ProtocolPolicy`, `TenantProtocolPolicy`, + `Explanation`, `AiLimits`, Fastmail `MaskedEmail`) aren't in the schema + at all: they are defined in `crates/jmap-proto/src/object/inbuxa_*.rs`. + +So the source of truth for *what exists* is the schema (for registry objects) +and `jmap-proto` (for inbuxa's own). Neither should carry the catalog: +**Decision proposed:** the catalog is a sidecar, and the schema and generated +code are read, never annotated, by it. The fork's existing hand edits to the +schema (layout entries, permissions) continue as today for the Compliance +pages and the new permissions, because the console's navigation comes from +the schema and there is no other way in; each is marked in the commit and +listed in the strip report as now. + +## 4. The catalog: format and location + +**Location:** `resources/privacy/catalog.toml`, a fork-owned file. The server +embeds it (`include_str!`) and parses it at start, as it does the schema. + +**Why a sidecar** rather than annotating in place: + +- The schema and registry code are upstream's generated output; annotations + there are overwritten or conflict on every import (SPEC.md §2.2, §2.3). +- Many sources aren't registry objects: log files, telemetry exporters, + inbuxa's key-space records, DNSBL and Pyzor, the in-memory cache. An + in-place annotation has nowhere to put them. +- One file is one diff to review when an import brings new fields, and it is + what the CI check and the strip report read. + +**Shape.** Three kinds of entry: + +```toml +# A registry object: a default for its properties, and the ones that differ. +[object."x:Account"] +default = "none" # properties not listed hold no personal data +whose = ["holder"] +where = ["data-store"] +scope = "tenant" +retention = "object-life" +[object."x:Account".properties] +name = ["identifier"] +emailAddress = ["identifier"] +aliases = ["identifier"] +description = ["content"] +credentials = ["credential", "network"] # secrets, allowedIps +locale = ["metadata"] + +# One of inbuxa's own JMAP objects (not in the schema). +[object."inbuxa:AuditEvent"] +default = "none" +whose = ["administrator", "holder", "correspondent"] +where = ["data-store"] +scope = "tenant" +retention = { setting = "inbuxa:AuditSettings.keepForDays" } +[object."inbuxa:AuditEvent".properties] +actor = ["identifier"] +remoteIp = ["network"] +changes = ["content"] +reason = ["content"] + +# A source that is no object: files, exporters, lookups, caches. +[source."log-file"] +categories = ["network", "identifier", "metadata"] +whose = ["correspondent", "holder", "administrator"] +where = ["log-file"] +scope = "server" +enabled_by = ["x:TracerLog.enable"] +captures = ["x:TracerLog.level", "x:TracerLog.events", "x:EventTracingLevel"] +retention = "unbounded" +leaves_host = false +written_by = ["crates/common/src/telemetry/tracers/log.rs"] + +[source."spam-dnsbl"] +categories = ["network", "identifier", "metadata"] +whose = ["correspondent"] +where = ["external"] +scope = "server" +enabled_by = ["x:SpamDnsblServer.enable"] +retention = "receiver" +leaves_host = true +written_by = ["crates/spam-filter/src/modules/dnsbl.rs"] +``` + +Rules: `retention` is `unbounded`, `object-life`, `receiver` (it left the +host), or `{ setting = "." }`; every setting named is a real +`x:` or `inbuxa:` property. Every object in the schema and every `inbuxa:` +object has an entry, even if only `default = "none"` (configuration objects +with nothing personal). The file holds facts only: no wording about legal +status. + +**Evaluation (Phase 3).** Each entry's `enabled_by`, `retention` and +`leaves_host` are evaluated against the live registry: a store pointed at a +remote backend makes everything in it `leaves_host = true`; an unset +retention setting reads `unbounded`; an external endpoint lists its URL's +host as a candidate processor. + +## 5. The CI check + +`tools/fork/privacy-check.py`, modeled on `name-check.py` and +`notice-check.py` (plain Python, no dependencies, exit 1 on findings). Wired +into the `fork-checks` job in `.gitea/workflows/ci.yml`, beside them. It fails +when: + +1. an object in `schema.json.gz` `fields`, or an `inbuxa:` object in + `crates/jmap-proto/src/object/`, has no catalog entry; +2. a property whose schema format is `emailAddress`, `ipAddress`, + `ipNetwork`, `secret`/`secretText`, or whose type is `x:SecretKey*` or + `x:HttpAuth`, is covered only by an object's `default = "none"` — those + must be classified explicitly, so a new personal field can't hide behind a + default; +3. an entry names an object, property or setting that no longer exists + (stale); +4. an entry uses a category, subject or location outside the vocabulary. + +`tools/fork/strip.py` gains `privacy_flags(tree)`, beside `schema_flags`: +objects and properties new in an import and not in the catalog, reported in +`STRIP-REPORT.md` under "Unclassified in the privacy catalog", the way +Enterprise flags are reported. Informational, as the Enterprise list is; CI +is what fails. + +Tests (Phase 2): the check passes on main; fails on a synthetic unclassified +field; fails on a synthetic `emailAddress` property covered only by a +default; fails on a stale entry. + +## Default profile + +**Today's defaults in a new install** that collect or send personal data: + +| Source | Collected | Retention | +|---|---|---| +| Traces + trace index | yes | 30 days | +| Log file | yes, info level | **unbounded** | +| Audit log | yes | 730 days | +| Spam training samples (whole messages) | yes | 180 days | +| Spam trainer state | yes, after 200 samples | **unbounded** | +| Automatic IP bans | yes | **unbounded** | +| Legacy-protocol last use | yes | **unbounded** | +| Received DMARC/TLS/ARF reports | yes | 30 days | +| DNSBL lookups (17 lists), Pyzor, URL-shortener fetches | yes | leave the host | +| Outbound DMARC aggregate and failure reports | yes, where the domain asks | leave the host | +| Trash | yes | 30 days | +| Undelete, deleted-account keeping | **off** | — | +| AI classification, Explain | **off** (no model) | — | +| Greylisting | **off** | — | + +**Proposed changes, new installs only**, each for John to decide. None is +made by this spec. + +| # | Change | Trade-off | +|---|---|---| +| D1 | A **log retention** setting on `x:TracerLog` (delete rotated files older than N days), default 30 days for new installs | Needs code (a new field, so a schema edit); older logs gone for troubleshooting; operators wanting longer set it | +| D2 | A default **ban period**, e.g. 30 days, for the four ban rates, and a purge of expired `x:BlockedIp` | A persistent attacker is re-banned after expiry rather than kept out; permanent bans of shared/NAT addresses stop being permanent | +| D3 | **Spam sample retention** 180 → 90 days | Fewer samples to retrain from; the classifier is retrained regularly, so the effect on accuracy is likely small (not measured) | +| D4 | **Pyzor off** by default | One fewer spam signal; no body digests leave the host | +| D5 | **msbl.org EBL off** by default (the list that receives hashed addresses) | One fewer signal on address-based spam; no hashed addresses leave the host | +| D6 | **Trace retention** 30 → 14 days | Shorter delivery history in **Emails › History** and for support | +| D7 | Webhooks default to `eventsPolicy = include` with no events, and never receive raw-I/O events unless listed by name | A webhook does nothing until events are chosen; closes finding 1 | + +Separately, **not defaults but gaps** a later spec should close (listed so +they aren't lost): clearing inbuxa's own records on account deletion +(finding 4); an expiry for the spam trainer state; a purge for failed tasks +(finding 10, once confirmed); removing export blobs when their link expires +(finding 9). + +## 6. The inventory, over JMAP (Phase 3) + +Under `urn:inbuxa:jmap`, read-only. + +### `inbuxa:DataInventory/get` + +A singleton, evaluated on request. + +Request: `{ accountId, ids: null | ["singleton"], properties? }`. + +Response `list[0]`: + +```json +{ + "id": "singleton", + "evaluatedAt": "2026-09-28T10:00:00Z", + "catalogVersion": "2026.9.28.3", + "sources": [ + { + "id": "log-file", + "kind": "source", + "categories": ["network", "identifier", "metadata"], + "whose": ["correspondent", "holder", "administrator"], + "where": ["log-file"], + "collected": true, + "retention": { "unbounded": true }, + "leavesHost": false, + "scope": "server", + "controlledBy": ["x:TracerLog.enable", "x:TracerLog.level"], + "endpoints": [] + }, + { + "id": "x:Trace", + "kind": "object", + "categories": ["network", "identifier", "metadata"], + "collected": true, + "retention": { "days": 30, "setting": "x:DataRetention.holdTracesFor" }, + "leavesHost": false, + "scope": "server", + "endpoints": [] + } + ], + "processors": [ + { "host": "public.pyzor.org", "receives": ["content"], "sources": ["spam-pyzor"] } + ] +} +``` + +`collected` is false when the source's switch is off; `retention` reads the +live setting; `leavesHost` is true when the source's location is a remote +store or an external endpoint; `processors` lists each external host once, +with what it receives — candidates, since whether a host is a processor in +law is the operator's determination. + +**Permission:** `sysComplianceGet`. **Tenant scoping:** inside a tenant, +`sources` holds only `scope = "tenant"` entries, evaluated with the tenant's +own settings where it has them (its protocol switches, its domains' stores +are the server's); server-scope sources and `processors` are left out, because +they describe the whole server. A tenant principal without the permission gets +`forbidden`. + +### `inbuxa:InventorySnapshot/get`, `/query` + +A dated copy of the evaluated inventory, taken when a setting the catalog +references changes (hooked where the audit log already sees registry writes) +and at most once a day otherwise. `query` filters by `after`/`before`, +newest first. Fields: `id`, `takenAt`, `trigger` (`setting-changed` with the +setting, or `daily`), `summary` (counts: sources collected, unbounded, +leaving the host, processors), and on `get` the full inventory as above. +Kept for `inbuxa:AuditSettings.keepForDays`, so history is as long as the +audit log's. Same permission and tenant scoping as the inventory. + +## 7. The compliance role + +### What it holds + +A new built-in role, **Compliance Officer**, at server level: + +| Permission | New? | Gives | +|---|---|---| +| `sysComplianceGet` | new | Overview, Data inventory, snapshots | +| `sysAuditGet`, `sysAuditExport` | existing | read and export the audit log, verify the chain | +| `sysLegalHoldGet`, `sysLegalHoldCreate`, `sysLegalHoldUpdate`, `sysLegalHoldExport` | existing | place, widen, release and export holds | +| `sysAccountLockGet` | existing | see locks and delegations | +| read (`*Get`, `*Query`) on accounts, groups, lists, domains, tenants, roles | existing | know who and what the records refer to | + +It holds **no** `*Create`/`*Update`/`*Destroy` on registry objects, no +`sysAuditSettingsUpdate`, no `impersonate`, no `fetchAnyBlob`. It can't +change a server setting. + +**Decision proposed on holds:** the officer *places and releases* holds, +because that is the job; every placing, widening and release is already +recorded with its reason, under the officer's own identity (AU-12), and a +release can't be undone silently (LH-10). If John prefers holds to need a +server administrator, drop Create/Update from the role. + +A **Tenant Compliance Officer** variant (inside a tenant: `sysComplianceGet`, +`sysAuditGet`, `sysAuditExport`, `sysAccountLockGet`, reads) is proposed +for later, since legal holds are server-only by the tenant ceiling (LH-13). +Open question 3. + +### Reaching existing servers + +New permissions get ids 673 onward and `COUNT` grows (`enums.rs`, +`enums_impl.rs`, and the schema's `Permission` enum, a marked hand edit as +before). `DefaultPermissions` places `sysComplianceGet` with superusers; +`granted_permissions.rs` `ADMIN_GRANTS` adds it once to stored administrator +roles, as it did for the audit and hold permissions. The **role object** +itself is seeded only on a fresh install (`defaults.rs`, `count == 0`); for +existing servers a one-time step modeled on `granted_permissions.rs` creates +it once, recorded so that deleting it sticks. That step is new code. + +### Keeping administrators reviewable + +What already holds: every administrator's change to the registry and to +inbuxa's own objects is recorded before it is allowed, under their identity, +with a reason where required; sign-ins by anyone holding `sys*` permissions +are recorded; records can't be deleted over JMAP; the chain is verifiable; +changing retention is itself recorded. + +What this adds: the compliance officer reads all of it, server administrators' +actions included, without being an administrator. The Overview (§8) puts on +top anything that weakens review: audit retention shortened, a tracer or the +audit export webhook removed, a change to who holds the compliance or +administrator roles, a hold released — each with who and when. + +What remains open: a server administrator can still shorten audit retention +to 90 days (recorded, and now surfaced), and anyone with shell access can edit +the store directly. Open question 4. + +## 8. The Compliance section in the console + +The navigation comes from the schema's `layouts` (`/api/schema`); a +hand-built page is a `CustomComponent/` link there, a render branch in +`MainContent.tsx`, and a visibility rule in `layout.ts` `checkSpecialLink`. +The section shows when at least one of its pages is visible to the person +(Phase 4 confirms the container behaves so with every child hidden). + +Planned navigation, in order: + +| Page | This task | Built as | Visible with | +|---|---|---|---| +| **Overview** | **builds** | hand-built (`CustomComponent/ComplianceOverview`) | `sysComplianceGet` | +| **Data inventory** | **builds** | hand-built (`CustomComponent/DataInventory`) | `sysComplianceGet` | +| Retention | later spec | likely hand-built over `x:DataRetention`, `x:SpamClassifier`, `x:TracerLog` and `inbuxa:AuditSettings`, since they're spread across objects | — | +| Legal holds | exists | hand-built | `sysLegalHoldGet` | +| Audit log | exists | hand-built | `sysAuditGet` | +| Locked accounts | exists | hand-built | `sysAccountLockGet` | +| Data subject requests | later spec | hand-built | — | +| Records and documents | later spec | could be schema-driven if it becomes a registry object | — | +| Jurisdiction packs | later spec | hand-built | — | + +**Decision proposed:** later pages are left out of the navigation until +built, not shown disabled: a disabled entry reads as a feature that exists. + +**Overview** shows the latest snapshot's findings as facts ("Log files are +kept with no limit", "Sent to a host outside this network: public.pyzor.org"), +the review items from §7, and the snapshot history (when and why the inventory +changed). **Data inventory** lists the evaluated sources, filterable by +source, category and location, with each external endpoint listed as a +candidate processor. UI text states facts and never claims a legal standard is met. + +## Contradictions with the docs and SPEC.md + +1. **docs, Security › "What the AI features do and do not send":** "message + content is never written to the logs". True at the default level. The + model's reply to the spam classifier is logged as `ai.llm-response` at + trace level (up to 1,024 characters, which can restate the message), so a + trace-level tracer writes it, and any webhook at its defaults receives it, + along with raw SMTP input (finding 1). +2. **docs, Audit log:** suggests a webhook to pass records to a SIEM, without + saying a webhook's `level` is ignored and its default policy sends every + event. +3. **docs, Monitoring:** "rotated daily" is right, but doesn't say old files + are never removed (finding 2). Not strictly a contradiction; an omission + that matters here. +4. **SPEC.md §2.3** calls `schema.json.gz` upstream's published schema and + the checklist for rebuilt features. It doesn't say the fork now hand-edits + it (layout, permissions); §2.2's strip step would overwrite those edits on + an import unless they're re-applied. Worth a sentence in SPEC.md. +5. **The schema's `TracingStore`/`MetricsStore` defaults** say disabled; first + boot turns both on. The docs describe the real behavior; the schema default + (what the console shows as the default) doesn't. + +The rest checked out: tracing 30 days, metrics 90 days, the Explain cache (in +memory, a day), audit retention (two years, minimum 90), undelete off by +default. + +## Open questions for John + +1. **The catalog's home and the schema edits.** Sidecar `catalog.toml` as + proposed, with the Compliance pages and new permission added to the schema + by hand as the audit and hold work did — or something else? +2. **Holds in the compliance role.** Can the officer place and release holds, + or only see and export them? +3. **A tenant compliance role** now, or after data subject requests? +4. **Audit retention floor.** Should shortening audit retention need a second + person, or is recording and surfacing it enough? +5. **Defaults D1–D7.** Which, if any, for new installs? +6. **Finding 1 (webhooks).** Fix now as a bug, separately from this work, or + wait for D7? +7. **Snapshots:** kept as long as the audit log, as proposed, or their own + setting? + +## Phases + +1. **This spec.** Stop for approval. +2. **The catalog and its check:** `resources/privacy/catalog.toml`, + `tools/fork/privacy-check.py` in `fork-checks`, the strip report section, + and the tests in §5. +3. **Server:** `sysComplianceGet` and the role (with the existing-server + step), `inbuxa:DataInventory`, `inbuxa:InventorySnapshot`, tests with + several configurations (defaults, a remote store, a hosted AI endpoint, + telemetry off), tenant scoping, refusal without the permission. +4. **Console:** Overview and Data inventory, the navigation entries, a PR + linking this spec. From a588a8aa7d5c05e5e34601411d05587d71ac697c Mon Sep 17 00:00:00 2001 From: John Coffey Date: Mon, 28 Sep 2026 05:57:24 -0700 Subject: [PATCH 2/6] Spec: record John's answers to the personal-data catalog questions The sidecar catalog; the Compliance Officer places and releases holds; the Tenant Compliance Officer is built now; shortening audit retention is recorded and surfaced, not gated on a second person; all seven new-install defaults, in Phase 3; the webhook finding fixed now as a bug; snapshots kept as long as the audit log. --- docs/spec/features/personal-data-catalog.md | 77 +++++++++++---------- 1 file changed, 41 insertions(+), 36 deletions(-) diff --git a/docs/spec/features/personal-data-catalog.md b/docs/spec/features/personal-data-catalog.md index e7ea6a8..d317279 100644 --- a/docs/spec/features/personal-data-catalog.md +++ b/docs/spec/features/personal-data-catalog.md @@ -1,7 +1,7 @@ # Feature spec: personal-data catalog, compliance role, and the Compliance section's first pages -Status: draft, 2026-09-28. Phase 1 of the GDPR auditor foundation: the -investigation and the design, for approval before anything is built. Not a +Status: approved 2026-09-28, with the answers under [Settled](#settled). +Phase 1 of the GDPR auditor foundation: the investigation and the design. Not a rebuild of an upstream feature, so it has no line in SPEC.md §4's table. ## Provenance @@ -252,7 +252,7 @@ of truth. What the investigation found: So the source of truth for *what exists* is the schema (for registry objects) and `jmap-proto` (for inbuxa's own). Neither should carry the catalog: -**Decision proposed:** the catalog is a sidecar, and the schema and generated +**Decision (Settled 1):** the catalog is a sidecar, and the schema and generated code are read, never annotated, by it. The fork's existing hand edits to the schema (layout entries, permissions) continue as today for the Compliance pages and the new permissions, because the console's navigation comes from @@ -390,8 +390,9 @@ default; fails on a stale entry. | AI classification, Explain | **off** (no model) | — | | Greylisting | **off** | — | -**Proposed changes, new installs only**, each for John to decide. None is -made by this spec. +**Changes for new installs only.** Settled (5): all seven are built, in +Phase 3; existing servers keep their settings. D7 is also covered by the bug +fix for finding 1 (Settled 6). | # | Change | Trade-off | |---|---|---| @@ -499,16 +500,16 @@ It holds **no** `*Create`/`*Update`/`*Destroy` on registry objects, no `sysAuditSettingsUpdate`, no `impersonate`, no `fetchAnyBlob`. It can't change a server setting. -**Decision proposed on holds:** the officer *places and releases* holds, -because that is the job; every placing, widening and release is already -recorded with its reason, under the officer's own identity (AU-12), and a -release can't be undone silently (LH-10). If John prefers holds to need a -server administrator, drop Create/Update from the role. +**Settled (2):** the officer *places and releases* holds, because that is +the job; every placing, widening and release is already recorded with its +reason, under the officer's own identity (AU-12), and a release can't be +undone silently (LH-10). -A **Tenant Compliance Officer** variant (inside a tenant: `sysComplianceGet`, -`sysAuditGet`, `sysAuditExport`, `sysAccountLockGet`, reads) is proposed -for later, since legal holds are server-only by the tenant ceiling (LH-13). -Open question 3. +A **Tenant Compliance Officer** (inside a tenant: `sysComplianceGet`, +`sysAuditGet`, `sysAuditExport`, `sysAccountLockGet`, reads) is built with +it (Settled 3). It has no hold permissions, since legal holds are +server-only by the tenant ceiling (LH-13), and it sees the tenant's slice of +the inventory only (§6). ### Reaching existing servers @@ -535,9 +536,10 @@ top anything that weakens review: audit retention shortened, a tracer or the audit export webhook removed, a change to who holds the compliance or administrator roles, a hold released — each with who and when. -What remains open: a server administrator can still shorten audit retention -to 90 days (recorded, and now surfaced), and anyone with shell access can edit -the store directly. Open question 4. +**Settled (4):** a server administrator can still shorten audit retention to +90 days; that is recorded and surfaced on the Overview, not gated on a second +person, since a server may have only one. Anyone with shell access can edit +the store directly; that is outside what the server can review. ## 8. The Compliance section in the console @@ -561,7 +563,7 @@ Planned navigation, in order: | Records and documents | later spec | could be schema-driven if it becomes a registry object | — | | Jurisdiction packs | later spec | hand-built | — | -**Decision proposed:** later pages are left out of the navigation until +**Decision:** later pages are left out of the navigation until built, not shown disabled: a disabled entry reads as a feature that exists. **Overview** shows the latest snapshot's findings as facts ("Log files are @@ -597,21 +599,22 @@ The rest checked out: tracing 30 days, metrics 90 days, the Explain cache (in memory, a day), audit retention (two years, minimum 90), undelete off by default. -## Open questions for John +## Settled -1. **The catalog's home and the schema edits.** Sidecar `catalog.toml` as - proposed, with the Compliance pages and new permission added to the schema - by hand as the audit and hold work did — or something else? -2. **Holds in the compliance role.** Can the officer place and release holds, - or only see and export them? -3. **A tenant compliance role** now, or after data subject requests? -4. **Audit retention floor.** Should shortening audit retention need a second - person, or is recording and surfacing it enough? -5. **Defaults D1–D7.** Which, if any, for new installs? -6. **Finding 1 (webhooks).** Fix now as a bug, separately from this work, or - wait for D7? -7. **Snapshots:** kept as long as the audit log, as proposed, or their own - setting? +John's answers, 2026-09-28, to the questions this spec asked: + +1. **The catalog is a sidecar**, `resources/privacy/catalog.toml`; the + Compliance pages and the new permission are hand-added to the schema as + the audit and hold work did. +2. **The Compliance Officer places and releases legal holds.** It's their + role. +3. **The Tenant Compliance Officer is built now**, with the server role + (Phase 3), not after data subject requests. +4. **Shortening audit retention is recorded and surfaced**, not gated on a + second person: a server may have only one. +5. **All seven defaults, D1–D7, for new installs.** Built in Phase 3. +6. **Finding 1 is fixed now**, as a bug, separately from this work. +7. **Snapshots are kept as long as the audit log** (`keepForDays`). ## Phases @@ -619,9 +622,11 @@ default. 2. **The catalog and its check:** `resources/privacy/catalog.toml`, `tools/fork/privacy-check.py` in `fork-checks`, the strip report section, and the tests in §5. -3. **Server:** `sysComplianceGet` and the role (with the existing-server - step), `inbuxa:DataInventory`, `inbuxa:InventorySnapshot`, tests with - several configurations (defaults, a remote store, a hosted AI endpoint, - telemetry off), tenant scoping, refusal without the permission. +3. **Server:** `sysComplianceGet`, the Compliance Officer and Tenant + Compliance Officer roles (with the existing-server step), + `inbuxa:DataInventory`, `inbuxa:InventorySnapshot`, and defaults D1–D7 + for new installs; tests with several configurations (defaults, a remote + store, a hosted AI endpoint, telemetry off), tenant scoping, refusal + without the permission. 4. **Console:** Overview and Data inventory, the navigation entries, a PR linking this spec. From 6945714aa9985a568dac410fd834550f7f3f145b Mon Sep 17 00:00:00 2001 From: John Coffey Date: Mon, 28 Sep 2026 06:38:37 -0700 Subject: [PATCH 3/6] Stop webhooks sending every event, message content included A webhook has a level (info by default) that nothing read: its events were chosen by its list and policy alone. With the default policy, exclude, and nothing listed, that meant every event type, including smtp.raw-input (the raw SMTP bytes, DATA included) and the model's reply to the spam classifier. The docs suggest a webhook to pass the audit log to a SIEM; set up that way it would have received whole messages. Found by the personal-data catalog investigation (finding 1). Now an include list is sent as named, whatever each event's level: naming an event is the choice. Otherwise a webhook gets only events at or above its level, as a tracer does, and never a protocol's raw input or output (IMAP, SMTP, POP3, ManageSieve, delivery, milter), which carries whole messages and credentials; those go out only when named. Tested: unit tests for the rule (level, raw I/O only when named, a named event below the level, custom event levels, a webhook's own errors); the telemetry system test, whose webhook names debug-level connection events and still receives them. --- crates/common/src/config/telemetry.rs | 93 ++++++++++++++++++++++++++- 1 file changed, 92 insertions(+), 1 deletion(-) diff --git a/crates/common/src/config/telemetry.rs b/crates/common/src/config/telemetry.rs index fa65202..beb360c 100644 --- a/crates/common/src/config/telemetry.rs +++ b/crates/common/src/config/telemetry.rs @@ -483,8 +483,16 @@ impl Tracers { }; // Parse webhook events + // inbuxa: personal-data catalog, finding 1: an include list is + // sent as named; otherwise a webhook honors its level as a + // tracer does, and never sends a protocol's raw input or + // output (whole messages) + let level = Level::from(hook.level); + let named = (hook.events_policy == EventPolicy::Include) + .then(|| hook.events.iter().copied().collect::>()) + .unwrap_or_default(); apply_events(hook.events, hook.events_policy, |event_type| { - if event_type != EventType::Telemetry(TelemetryEvent::WebhookError) { + if webhook_wants(event_type, level, &custom_levels, &named) { tracer.interests.set(event_type); global_interests.set(event_type); } @@ -743,6 +751,31 @@ fn tracer_settings(tracer: &Tracer) -> u64 { settings_hash(&tracer) } +/// inbuxa: whether a webhook at `level` receives this event type. Its own +/// error event never, or a failing webhook would report itself to itself. +/// An event `named` in an include list always: naming it is the choice. +/// Otherwise (the exclude policy, the default) only events at or above its +/// level, as for a tracer, and never a protocol's raw input or output, which +/// carries whole messages and credentials. +fn webhook_wants( + event_type: EventType, + level: Level, + custom_levels: &AHashMap, + named: &AHashSet, +) -> bool { + if event_type == EventType::Telemetry(TelemetryEvent::WebhookError) { + return false; + } + if named.contains(&event_type) { + return true; + } + let event_level = custom_levels + .get(&event_type) + .copied() + .unwrap_or(event_type.level()); + level.is_contained(event_level) && !event_type.is_raw_io() +} + fn webhook_settings(hook: &WebHook) -> u64 { let mut hook = hook.clone(); in_place_reset!(hook); @@ -804,3 +837,61 @@ impl std::fmt::Debug for OtelMetrics { .finish() } } + +#[cfg(test)] +mod tests { + use super::*; + use trc::{AuthEvent, SmtpEvent}; + + fn wants(event: EventType, level: Level, named: &[EventType]) -> bool { + webhook_wants( + event, + level, + &AHashMap::new(), + &named.iter().copied().collect(), + ) + } + + #[test] + fn a_webhook_honors_its_level() { + let success = EventType::Auth(AuthEvent::Success); + assert!(wants(success, Level::Info, &[])); + assert!(!wants(success, Level::Error, &[]), "info is below error"); + } + + #[test] + fn raw_io_goes_out_only_when_named() { + let raw = EventType::Smtp(SmtpEvent::RawInput); + assert!(raw.is_raw_io()); + // Not with the exclude policy, even at trace + assert!(!wants(raw, Level::Info, &[])); + assert!(!wants(raw, Level::Trace, &[])); + // Named in an include list, whatever the level + assert!(wants(raw, Level::Info, &[raw])); + } + + #[test] + fn a_named_event_is_sent_whatever_its_level() { + let start = EventType::Smtp(SmtpEvent::ConnectionStart); + assert!(!Level::Info.is_contained(start.level()), "below info"); + assert!(!wants(start, Level::Info, &[])); + assert!(wants(start, Level::Info, &[start])); + } + + #[test] + fn a_custom_level_counts() { + let start = EventType::Smtp(SmtpEvent::ConnectionStart); + let custom = [(start, Level::Info)].into_iter().collect::>(); + assert!(webhook_wants(start, Level::Info, &custom, &AHashSet::new())); + // Raw I/O raised to info still needs naming + let raw = EventType::Smtp(SmtpEvent::RawInput); + let custom = [(raw, Level::Info)].into_iter().collect::>(); + assert!(!webhook_wants(raw, Level::Info, &custom, &AHashSet::new())); + } + + #[test] + fn a_webhook_never_hears_its_own_errors() { + let own = EventType::Telemetry(TelemetryEvent::WebhookError); + assert!(!wants(own, Level::Trace, &[own])); + } +} From a8dde68800f5d64bea671df6138d78e7e4994356 Mon Sep 17 00:00:00 2001 From: John Coffey Date: Mon, 28 Sep 2026 06:54:34 -0700 Subject: [PATCH 4/6] Add the personal-data catalog and the check that keeps it true Phase 2 of the personal-data catalog spec. resources/privacy/catalog.toml classifies every object in the schema (316) and inbuxa's own JMAP objects (12): each property that can hold personal data, with its categories, and for objects that hold any, whose data it is, where it lives, its scope and what bounds its retention (a named setting where there is one). Twenty sources that are no object -- the log file, exporters, webhooks, spam lookups, the Explain cache, relays and hooks, push, legacy-use records -- carry the same facts plus the settings that turn them on, whether the data leaves the host, and the code that writes it. Classifications of objects that hold data about people are from the spec's source map; the rest are typed from the schema alone (address, IP, secret). tools/fork/privacy-check.py fails CI when an object or inbuxa object has no entry, when a property the schema types as an address, IP or secret is left to its object's default, when an entry names an object, property, setting or code path that is gone, or when it uses a word outside the catalog's vocabulary. --unlisted prints starting entries. strip.py's report gains "Unclassified in the privacy catalog": objects and fields new in an import and not classified, informational like the Enterprise flags. Tested: 13 unit tests (tools/fork/tests): the check passes on this tree; fails on an unclassified object, an address hidden behind a default, a secret in a set or object reference, stale properties, objects, settings and code paths, an unlisted inbuxa object and a word outside the vocabulary; --unlisted's entries; and the strip report on a synthetic import. The check and the tests run in the fork-checks job. --- .gitea/workflows/ci.yml | 6 + resources/privacy/catalog.toml | 2090 ++++++++++++++++++++++++ tools/fork/README.md | 19 + tools/fork/privacy-check.py | 228 +++ tools/fork/strip.py | 44 +- tools/fork/tests/test_privacy_check.py | 177 ++ 6 files changed, 2563 insertions(+), 1 deletion(-) create mode 100644 resources/privacy/catalog.toml create mode 100755 tools/fork/privacy-check.py create mode 100644 tools/fork/tests/test_privacy_check.py diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index e3541c9..917908b 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -40,6 +40,12 @@ jobs: # nothing. CI never sees the difference; a release does. - if: always() run: python3 tools/fork/context-check.py + # The personal-data catalog must classify every object and field the + # schema has, and name nothing that is gone. + - if: always() + run: python3 tools/fork/privacy-check.py + - if: always() + run: python3 -m unittest discover -s tools/fork/tests build: # Either runner (host1 or host2): the build needs no docker socket. diff --git a/resources/privacy/catalog.toml b/resources/privacy/catalog.toml new file mode 100644 index 0000000..f647a70 --- /dev/null +++ b/resources/privacy/catalog.toml @@ -0,0 +1,2090 @@ +# SPDX-FileCopyrightText: 2026 Coffey Labs +# SPDX-License-Identifier: AGPL-3.0-only +# +# The personal-data catalog (docs/spec/features/personal-data-catalog.md). +# +# Every place the server can store or send personal data, as possibilities: +# what a particular server holds depends on its settings, and the server +# evaluates this file against them. Facts only; nothing here judges. +# +# Three kinds of entry: +# [object."x:Name"] a registry object from resources/schema/schema.json.gz +# [object."inbuxa:Name"] one of inbuxa's own JMAP objects (crates/jmap-proto) +# [source."name"] anything that is no object: files, exporters, lookups +# +# An object's `default = "none"` covers every property not listed under its +# `.properties`. A property the schema types as an address, an IP or a secret +# is always listed. tools/fork/privacy-check.py enforces both, and that every +# object and setting named here exists. + +[vocabulary] +categories = ["identifier", "contact", "network", "content", "metadata", "credential"] +whose = ["holder", "correspondent", "administrator"] +where = ["data-store", "blob-store", "search-store", "in-memory-store", "memory", "log-file", "external"] +scope = ["tenant", "server"] +retention = ["unbounded", "object-life", "receiver"] + +# --------------------------------------------------------------------------- +# inbuxa's own objects. `file` names the jmap-proto source whose property +# names the check reads. + +[object."inbuxa:AuditEvent"] +file = "inbuxa_audit.rs" +default = "none" +whose = ["administrator", "holder", "correspondent"] +where = ["data-store"] +scope = "tenant" +retention = { setting = "inbuxa:AuditSettings.keepForDays" } +[object."inbuxa:AuditEvent".properties] +actor = ["identifier"] +via = ["identifier"] +remoteIp = ["network"] +target = ["identifier"] +changes = ["content"] +details = ["content"] +reason = ["content"] +at = ["metadata"] + +[object."inbuxa:AuditExport"] +file = "inbuxa_audit.rs" +default = "none" +whose = ["administrator", "holder", "correspondent"] +where = ["blob-store"] +scope = "tenant" +retention = { setting = "x:Jmap.uploadTtl" } +[object."inbuxa:AuditExport".properties] +blobId = ["identifier", "network", "content"] +filter = ["identifier"] + +[object."inbuxa:AuditSettings"] +file = "inbuxa_audit.rs" +default = "none" + +[object."inbuxa:AuditVerification"] +file = "inbuxa_audit.rs" +default = "none" + +[object."inbuxa:AccountLock"] +file = "inbuxa_account_lock.rs" +default = "none" +whose = ["holder", "administrator"] +where = ["data-store"] +scope = "tenant" +retention = "unbounded" +[object."inbuxa:AccountLock".properties] +accountId = ["identifier"] +name = ["identifier"] +reason = ["content"] +lockedAt = ["metadata"] +lockedBy = ["identifier"] +delegates = ["identifier"] + +[object."inbuxa:LegalHold"] +file = "inbuxa_legal_hold.rs" +default = "none" +whose = ["administrator", "holder"] +where = ["data-store"] +scope = "server" +retention = "unbounded" +[object."inbuxa:LegalHold".properties] +name = ["content"] +reference = ["content"] +description = ["content"] +scope = ["identifier"] +reason = ["content"] +placedBy = ["identifier"] +releasedBy = ["identifier"] +releaseReason = ["content"] + +[object."inbuxa:HoldExport"] +file = "inbuxa_hold_export.rs" +default = "none" +whose = ["holder", "correspondent", "administrator"] +where = ["data-store", "blob-store"] +scope = "server" +retention = "unbounded" +[object."inbuxa:HoldExport".properties] +accountIds = ["identifier"] +reason = ["content"] +createdBy = ["identifier"] +blobId = ["content", "identifier", "contact", "metadata"] + +[object."inbuxa:DeletedAccount"] +file = "inbuxa_deleted_account.rs" +default = "none" +whose = ["holder"] +where = ["data-store"] +scope = "tenant" +retention = { setting = "x:DataRetention.archiveDeletedAccountsFor" } +[object."inbuxa:DeletedAccount".properties] +name = ["identifier"] +addresses = ["identifier"] +password = ["credential"] +deletedAt = ["metadata"] +keptUntil = ["metadata"] + +[object."inbuxa:Explanation"] +file = "inbuxa_explanation.rs" +default = "none" +whose = ["correspondent", "holder"] +where = ["memory", "external"] +scope = "server" +retention = "receiver" +[object."inbuxa:Explanation".properties] +subject = ["identifier", "network", "metadata"] +text = ["identifier", "network", "metadata"] +preparedFor = ["identifier"] + +[object."inbuxa:ProtocolPolicy"] +file = "inbuxa_protocol_policy.rs" +default = "none" +whose = ["administrator", "holder"] +where = ["data-store"] +scope = "server" +retention = "unbounded" +[object."inbuxa:ProtocolPolicy".properties] +changedBy = ["identifier"] +recentLegacyUse = ["identifier", "metadata"] + +[object."inbuxa:TenantProtocolPolicy"] +file = "inbuxa_tenant_protocol_policy.rs" +default = "none" +whose = ["administrator", "holder"] +where = ["data-store"] +scope = "tenant" +retention = "unbounded" +[object."inbuxa:TenantProtocolPolicy".properties] +changedBy = ["identifier"] +recentLegacyUse = ["identifier", "metadata"] + +[object."inbuxa:AiLimits"] +file = "inbuxa_ai_limits.rs" +default = "none" + +# --------------------------------------------------------------------------- +# Sources that are no object. Settings are `.`: a schema +# field object, or one of inbuxa's own. + +[source."mail-and-groupware"] +categories = ["content", "identifier", "contact", "metadata"] +whose = ["holder", "correspondent"] +where = ["data-store", "blob-store"] +scope = "tenant" +retention = "unbounded" +captures = ["x:DataRetention.expungeTrashAfter", "x:DataRetention.expungeSubmissionsAfter"] +leaves_host = false +written_by = ["crates/email/src/message/ingest.rs", "crates/groupware/src/calendar/storage.rs", "crates/groupware/src/contact/storage.rs", "crates/groupware/src/file/storage.rs"] + +[source."full-text-index"] +categories = ["content", "identifier", "contact", "metadata"] +whose = ["holder", "correspondent"] +where = ["search-store"] +scope = "tenant" +retention = "object-life" +enabled_by = ["x:Search.indexEmail", "x:Search.indexCalendar", "x:Search.indexContacts"] +captures = ["x:Search.indexEmailFields", "x:Search.indexCalendarFields", "x:Search.indexContactFields"] +leaves_host = false +written_by = ["crates/services/src/task_manager/index.rs"] + +[source."trace-index"] +categories = ["network", "identifier", "metadata"] +whose = ["correspondent", "holder"] +where = ["search-store"] +scope = "server" +retention = { setting = "x:DataRetention.holdTracesFor" } +enabled_by = ["x:Search.indexTelemetry"] +captures = ["x:Search.indexTracingFields"] +leaves_host = false +written_by = ["crates/services/src/task_manager/index.rs"] + +[source."log-file"] +categories = ["network", "identifier", "metadata"] +whose = ["correspondent", "holder", "administrator"] +where = ["log-file"] +scope = "server" +retention = "unbounded" +enabled_by = ["x:TracerLog.enable"] +captures = ["x:TracerLog.level", "x:TracerLog.events", "x:EventTracingLevel.level"] +leaves_host = false +written_by = ["crates/common/src/telemetry/tracers/log.rs"] + +[source."console-and-journal"] +categories = ["network", "identifier", "metadata"] +whose = ["correspondent", "holder", "administrator"] +where = ["log-file"] +scope = "server" +retention = "receiver" +enabled_by = ["x:TracerStdout.enable"] +captures = ["x:TracerStdout.level"] +leaves_host = false +written_by = ["crates/common/src/telemetry/tracers/stdout.rs", "crates/common/src/telemetry/tracers/journald.rs"] + +[source."otel-tracer"] +categories = ["network", "identifier", "metadata"] +whose = ["correspondent", "holder", "administrator"] +where = ["external"] +scope = "server" +retention = "receiver" +enabled_by = ["x:TracerOtelHttp.enable", "x:TracerOtelGrpc.enable"] +captures = ["x:TracerOtelHttp.level", "x:TracerOtelGrpc.level"] +leaves_host = true +written_by = ["crates/common/src/telemetry/tracers/otel.rs"] + +[source."webhooks"] +categories = ["network", "identifier", "metadata", "content"] +whose = ["correspondent", "holder", "administrator"] +where = ["external"] +scope = "server" +retention = "receiver" +enabled_by = ["x:WebHook.enable"] +captures = ["x:WebHook.level", "x:WebHook.events", "x:WebHook.eventsPolicy"] +leaves_host = true +written_by = ["crates/common/src/telemetry/webhooks/mod.rs"] + +[source."spam-trainer-state"] +categories = ["metadata"] +whose = ["correspondent", "holder"] +where = ["blob-store"] +scope = "server" +retention = "unbounded" +enabled_by = ["x:SpamClassifier.model"] +leaves_host = false +written_by = ["crates/spam-filter/src/modules/classifier.rs"] + +[source."spam-llm"] +categories = ["content"] +whose = ["correspondent"] +where = ["external"] +scope = "server" +retention = "receiver" +enabled_by = ["x:SpamLlmProperties.modelId"] +captures = ["inbuxa:AiLimits.maxContentBytes"] +leaves_host = true +written_by = ["crates/spam-filter/src/analysis/llm.rs", "crates/common/src/enterprise/llm.rs"] + +[source."explain-cache"] +categories = ["identifier", "network", "metadata"] +whose = ["correspondent", "holder"] +where = ["memory"] +scope = "server" +retention = "unbounded" +enabled_by = ["inbuxa:AiLimits.explainEnabled"] +leaves_host = false +written_by = ["crates/features/src/ai/explain/memory.rs"] + +[source."spam-dnsbl"] +categories = ["network", "identifier", "metadata"] +whose = ["correspondent"] +where = ["external"] +scope = "server" +retention = "receiver" +enabled_by = ["x:SpamDnsblServerIp.enable", "x:SpamDnsblServerDomain.enable", "x:SpamDnsblServerEmail.enable", "x:SpamDnsblServerUrl.enable"] +leaves_host = true +written_by = ["crates/spam-filter/src/modules/dnsbl.rs"] + +[source."spam-pyzor"] +categories = ["content"] +whose = ["correspondent"] +where = ["external"] +scope = "server" +retention = "receiver" +enabled_by = ["x:SpamPyzor.enable"] +leaves_host = true +written_by = ["crates/spam-filter/src/modules/pyzor.rs"] + +[source."spam-url-redirects"] +categories = ["network"] +whose = ["correspondent", "holder"] +where = ["external"] +scope = "server" +retention = "receiver" +leaves_host = true +written_by = ["crates/spam-filter/src/analysis/url.rs"] + +[source."mta-milter-and-hooks"] +categories = ["network", "identifier", "content"] +whose = ["holder", "correspondent"] +where = ["external"] +scope = "server" +retention = "receiver" +enabled_by = ["x:MtaMilter.enable", "x:MtaHook.enable"] +leaves_host = true +written_by = ["crates/smtp/src/inbound/hooks/mod.rs"] + +[source."relay"] +categories = ["content", "identifier", "metadata"] +whose = ["holder", "correspondent"] +where = ["external"] +scope = "server" +retention = "receiver" +leaves_host = true +written_by = ["crates/smtp/src/outbound/delivery.rs"] + +[source."push-subscriptions"] +categories = ["network", "credential", "content"] +whose = ["holder"] +where = ["data-store", "external"] +scope = "tenant" +retention = "object-life" +captures = ["x:Jmap.maxSubscriptions"] +leaves_host = true +written_by = ["crates/jmap/src/push/set.rs", "crates/services/src/state_manager/http.rs"] + +[source."rate-limit-and-greylist"] +categories = ["network", "identifier"] +whose = ["holder", "correspondent"] +where = ["in-memory-store"] +scope = "server" +retention = "object-life" +enabled_by = ["x:SpamSettings.greylistFor"] +leaves_host = false +written_by = ["crates/common/src/auth/rate_limit.rs", "crates/smtp/src/inbound/rcpt.rs"] + +[source."legacy-last-use"] +categories = ["metadata"] +whose = ["holder"] +where = ["data-store"] +scope = "tenant" +retention = "unbounded" +leaves_host = false +written_by = ["crates/features/src/security/legacy_use.rs"] + +[source."masked-address-records"] +categories = ["identifier", "metadata"] +whose = ["holder"] +where = ["data-store"] +scope = "tenant" +retention = "unbounded" +captures = ["x:Email.maxMaskedAddresses"] +leaves_host = false +written_by = ["crates/features/src/masked_email/data.rs"] + +[source."outbound-reports"] +categories = ["network", "identifier", "content"] +whose = ["correspondent"] +where = ["external"] +scope = "server" +retention = "receiver" +captures = ["x:DmarcReportSettings.aggregateSendFrequency", "x:DmarcReportSettings.failureSendFrequency", "x:TlsReportSettings.sendFrequency"] +leaves_host = true +written_by = ["crates/smtp/src/reporting/dmarc.rs", "crates/smtp/src/reporting/tls.rs"] + +# --------------------------------------------------------------------------- +# Registry objects, one per schema field object. + +[object."AddressBook"] +default = "none" +whose = ["holder"] +where = ["data-store"] +scope = "tenant" +retention = "object-life" +[object."AddressBook".properties] +description = ["content"] +name = ["content"] +shareWith = ["identifier"] + +[object."AddressBookRights"] +default = "none" + +[object."Calendar"] +default = "none" +whose = ["holder"] +where = ["data-store"] +scope = "tenant" +retention = "object-life" +[object."Calendar".properties] +description = ["content"] +name = ["content"] +shareWith = ["identifier"] +timeZone = ["metadata"] + +[object."CalendarRights"] +default = "none" + +[object."Mailbox"] +default = "none" +whose = ["holder"] +where = ["data-store"] +scope = "tenant" +retention = "object-life" +[object."Mailbox".properties] +name = ["content"] +shareWith = ["identifier"] + +[object."MailboxRights"] +default = "none" + +[object."Principal"] +default = "none" +whose = ["holder"] +where = ["data-store"] +scope = "tenant" +retention = "object-life" +[object."Principal".properties] +description = ["content"] +email = ["identifier"] +name = ["identifier"] +timeZone = ["metadata"] + +[object."SieveScript"] +default = "none" +whose = ["holder"] +where = ["data-store", "blob-store"] +scope = "tenant" +retention = "object-life" +[object."SieveScript".properties] +blobId = ["content"] +name = ["content"] + +[object."VacationResponse"] +default = "none" +whose = ["holder"] +where = ["data-store"] +scope = "tenant" +retention = "object-life" +[object."VacationResponse".properties] +fromDate = ["metadata"] +htmlBody = ["content"] +subject = ["content"] +textBody = ["content"] +toDate = ["metadata"] + +[object."x:AccountPassword"] +default = "none" +whose = ["holder"] +where = ["data-store"] +scope = "tenant" +retention = "object-life" +[object."x:AccountPassword".properties] +currentSecret = ["credential"] +otpAuth = ["credential"] +secret = ["credential"] + +[object."x:AccountSettings"] +default = "none" +whose = ["holder"] +where = ["data-store"] +scope = "tenant" +retention = "object-life" +[object."x:AccountSettings".properties] +description = ["content"] +locale = ["metadata"] +timeZone = ["metadata"] + +[object."x:AcmeProvider"] +default = "none" +whose = ["administrator"] +where = ["data-store"] +scope = "server" +retention = "object-life" +[object."x:AcmeProvider".properties] +accountKey = ["credential"] +contact = ["contact"] +eabHmacKey = ["credential"] +eabKeyId = ["credential"] + +[object."x:AddressBook"] +default = "none" + +[object."x:AiModel"] +default = "none" +[object."x:AiModel".properties] +httpAuth = ["credential"] + +[object."x:Alert"] +default = "none" + +[object."x:AlertEmailProperties"] +default = "none" +whose = ["administrator"] +where = ["data-store"] +scope = "server" +retention = "object-life" +[object."x:AlertEmailProperties".properties] +fromAddress = ["contact"] +fromName = ["contact"] +to = ["contact"] + +[object."x:AlertEventProperties"] +default = "none" + +[object."x:AllowedIp"] +default = "none" +whose = ["administrator"] +where = ["data-store"] +scope = "server" +retention = { setting = "x:AllowedIp.expiresAt" } +[object."x:AllowedIp".properties] +address = ["network"] +createdAt = ["metadata"] +expiresAt = ["metadata"] +reason = ["content"] + +[object."x:ApiKey"] +default = "none" +whose = ["holder"] +where = ["data-store"] +scope = "tenant" +retention = "object-life" +[object."x:ApiKey".properties] +allowedIps = ["network"] +createdAt = ["metadata"] +description = ["content"] +expiresAt = ["metadata"] +secret = ["credential"] + +[object."x:AppPassword"] +default = "none" +whose = ["holder"] +where = ["data-store"] +scope = "tenant" +retention = "object-life" +[object."x:AppPassword".properties] +allowedIps = ["network"] +createdAt = ["metadata"] +description = ["content"] +expiresAt = ["metadata"] +secret = ["credential"] + +[object."x:Application"] +default = "none" + +[object."x:ArchivedCalendarEvent"] +default = "none" +whose = ["holder", "correspondent"] +where = ["data-store", "blob-store"] +scope = "tenant" +retention = { setting = "x:DataRetention.archiveDeletedItemsFor" } +[object."x:ArchivedCalendarEvent".properties] +archivedAt = ["metadata"] +archivedUntil = ["metadata"] +blobId = ["content"] +createdAt = ["metadata"] +startTime = ["metadata"] +title = ["content"] + +[object."x:ArchivedContactCard"] +default = "none" +whose = ["holder", "correspondent"] +where = ["data-store", "blob-store"] +scope = "tenant" +retention = { setting = "x:DataRetention.archiveDeletedItemsFor" } +[object."x:ArchivedContactCard".properties] +archivedAt = ["metadata"] +archivedUntil = ["metadata"] +blobId = ["contact"] +createdAt = ["metadata"] +name = ["contact"] + +[object."x:ArchivedEmail"] +default = "none" +whose = ["holder", "correspondent"] +where = ["data-store", "blob-store"] +scope = "tenant" +retention = { setting = "x:DataRetention.archiveDeletedItemsFor" } +[object."x:ArchivedEmail".properties] +archivedAt = ["metadata"] +archivedUntil = ["metadata"] +blobId = ["content"] +from = ["identifier"] +receivedAt = ["metadata"] +size = ["metadata"] +subject = ["content"] + +[object."x:ArchivedFileNode"] +default = "none" +whose = ["holder"] +where = ["data-store", "blob-store"] +scope = "tenant" +retention = { setting = "x:DataRetention.archiveDeletedItemsFor" } +[object."x:ArchivedFileNode".properties] +archivedAt = ["metadata"] +archivedUntil = ["metadata"] +blobId = ["content"] +createdAt = ["metadata"] +name = ["content"] + +[object."x:ArchivedSieveScript"] +default = "none" +whose = ["holder"] +where = ["data-store", "blob-store"] +scope = "tenant" +retention = { setting = "x:DataRetention.archiveDeletedItemsFor" } +[object."x:ArchivedSieveScript".properties] +archivedAt = ["metadata"] +archivedUntil = ["metadata"] +blobId = ["content"] +content = ["content"] +createdAt = ["metadata"] +name = ["content"] + +[object."x:ArfExternalReport"] +default = "none" +whose = ["correspondent"] +where = ["data-store"] +scope = "tenant" +retention = { setting = "x:DataRetention.holdMtaReportsFor" } +[object."x:ArfExternalReport".properties] +from = ["identifier"] +receivedAt = ["metadata"] +subject = ["content"] +to = ["identifier"] + +[object."x:ArfFeedbackReport"] +default = "none" +whose = ["correspondent"] +where = ["data-store"] +scope = "tenant" +retention = { setting = "x:DataRetention.holdMtaReportsFor" } +[object."x:ArfFeedbackReport".properties] +arrivalDate = ["metadata"] +dkimIdentity = ["identifier"] +headers = ["content"] +message = ["content"] +originalEnvelopeId = ["metadata"] +originalMailFrom = ["identifier"] +originalRcptTo = ["identifier"] +reportedDomains = ["identifier"] +reportedUris = ["content"] +sourceIp = ["network"] +sourcePort = ["network"] +userAgent = ["network"] + +[object."x:AsnDns"] +default = "none" + +[object."x:AsnResource"] +default = "none" +[object."x:AsnResource".properties] +httpAuth = ["credential"] + +[object."x:Authentication"] +default = "none" + +[object."x:AzureStore"] +default = "none" +[object."x:AzureStore".properties] +accessKey = ["credential"] +sasToken = ["credential"] + +[object."x:BlockedIp"] +default = "none" +whose = ["correspondent", "holder"] +where = ["data-store"] +scope = "server" +retention = "unbounded" +[object."x:BlockedIp".properties] +address = ["network"] +createdAt = ["metadata"] +expiresAt = ["metadata"] +reason = ["content"] + +[object."x:Bootstrap"] +default = "none" +whose = ["administrator"] +where = ["data-store"] +scope = "server" +retention = "object-life" +[object."x:Bootstrap".properties] +defaultDomain = ["identifier"] +secret = ["credential"] +username = ["identifier"] + +[object."x:Cache"] +default = "none" + +[object."x:Calendar"] +default = "none" + +[object."x:CalendarAlarm"] +default = "none" +whose = ["administrator"] +where = ["data-store"] +scope = "server" +retention = "object-life" +[object."x:CalendarAlarm".properties] +fromEmail = ["contact"] +fromName = ["contact"] + +[object."x:CalendarScheduling"] +default = "none" + +[object."x:Certificate"] +default = "none" +[object."x:Certificate".properties] +privateKey = ["credential"] + +[object."x:CertificateManagementProperties"] +default = "none" + +[object."x:ClusterListenerGroupProperties"] +default = "none" + +[object."x:ClusterNode"] +default = "none" + +[object."x:ClusterRole"] +default = "none" + +[object."x:ClusterTaskGroupProperties"] +default = "none" + +[object."x:CredentialPermissionsList"] +default = "none" + +[object."x:CronDaily"] +default = "none" + +[object."x:CronHourly"] +default = "none" + +[object."x:CronWeekly"] +default = "none" + +[object."x:CustomRoles"] +default = "none" + +[object."x:DataRetention"] +default = "none" + +[object."x:DeliveryError"] +default = "none" + +[object."x:Dkim1Signature"] +default = "none" +[object."x:Dkim1Signature".properties] +privateKey = ["credential"] + +[object."x:Dkim2Signature"] +default = "none" +[object."x:Dkim2Signature".properties] +privateKey = ["credential"] + +[object."x:DkimManagementProperties"] +default = "none" + +[object."x:DkimReportSettings"] +default = "none" + +[object."x:DmarcDkimResult"] +default = "none" + +[object."x:DmarcExtension"] +default = "none" + +[object."x:DmarcExternalReport"] +default = "none" +whose = ["correspondent"] +where = ["data-store"] +scope = "tenant" +retention = { setting = "x:DataRetention.holdMtaReportsFor" } +[object."x:DmarcExternalReport".properties] +from = ["identifier"] +receivedAt = ["metadata"] +subject = ["content"] +to = ["identifier"] + +[object."x:DmarcInternalReport"] +default = "none" +whose = ["correspondent"] +where = ["data-store"] +scope = "server" +retention = "receiver" +[object."x:DmarcInternalReport".properties] +domain = ["identifier"] +rua = ["identifier"] + +[object."x:DmarcPolicyOverrideReason"] +default = "none" + +[object."x:DmarcReport"] +default = "none" +whose = ["correspondent"] +where = ["data-store"] +scope = "tenant" +retention = { setting = "x:DataRetention.holdMtaReportsFor" } +[object."x:DmarcReport".properties] +email = ["contact"] +extraContactInfo = ["contact"] +orgName = ["identifier"] + +[object."x:DmarcReportRecord"] +default = "none" +whose = ["correspondent"] +where = ["data-store"] +scope = "tenant" +retention = { setting = "x:DataRetention.holdMtaReportsFor" } +[object."x:DmarcReportRecord".properties] +count = ["metadata"] +envelopeFrom = ["identifier"] +envelopeTo = ["identifier"] +headerFrom = ["identifier"] +sourceIp = ["network"] + +[object."x:DmarcReportSettings"] +default = "none" + +[object."x:DmarcSpfResult"] +default = "none" + +[object."x:DmarcTroubleshoot"] +default = "none" +whose = ["correspondent"] +where = ["memory"] +scope = "server" +retention = "unbounded" +[object."x:DmarcTroubleshoot".properties] +ehloDomain = ["network"] +ipRevPtr = ["network"] +mailFrom = ["identifier"] +message = ["content"] +remoteIp = ["network"] +to = ["identifier"] + +[object."x:DmarcTroubleshootDetails"] +default = "none" + +[object."x:DnsCustomResolver"] +default = "none" +[object."x:DnsCustomResolver".properties] +address = ["network"] + +[object."x:DnsManagementProperties"] +default = "none" + +[object."x:DnsResolverCommon"] +default = "none" + +[object."x:DnsResolverCustom"] +default = "none" + +[object."x:DnsResolverTls"] +default = "none" + +[object."x:DnsServerAlidns"] +default = "none" +[object."x:DnsServerAlidns".properties] +secretKey = ["credential"] +securityToken = ["credential"] + +[object."x:DnsServerAutodns"] +default = "none" +[object."x:DnsServerAutodns".properties] +password = ["credential"] + +[object."x:DnsServerAzureDns"] +default = "none" +[object."x:DnsServerAzureDns".properties] +clientSecret = ["credential"] + +[object."x:DnsServerBaiduCloud"] +default = "none" +[object."x:DnsServerBaiduCloud".properties] +secretKey = ["credential"] + +[object."x:DnsServerBluecatV2"] +default = "none" +[object."x:DnsServerBluecatV2".properties] +password = ["credential"] + +[object."x:DnsServerClouDns"] +default = "none" +[object."x:DnsServerClouDns".properties] +password = ["credential"] + +[object."x:DnsServerCloud"] +default = "none" +[object."x:DnsServerCloud".properties] +secret = ["credential"] + +[object."x:DnsServerCloudflare"] +default = "none" +[object."x:DnsServerCloudflare".properties] +secret = ["credential"] + +[object."x:DnsServerConstellix"] +default = "none" +[object."x:DnsServerConstellix".properties] +secretKey = ["credential"] + +[object."x:DnsServerCpanel"] +default = "none" +[object."x:DnsServerCpanel".properties] +token = ["credential"] + +[object."x:DnsServerDnsMadeEasy"] +default = "none" +[object."x:DnsServerDnsMadeEasy".properties] +secret = ["credential"] + +[object."x:DnsServerDnsimple"] +default = "none" +[object."x:DnsServerDnsimple".properties] +authToken = ["credential"] +secret = ["credential"] + +[object."x:DnsServerDomeneshop"] +default = "none" +[object."x:DnsServerDomeneshop".properties] +secret = ["credential"] + +[object."x:DnsServerEasyDns"] +default = "none" +[object."x:DnsServerEasyDns".properties] +key = ["credential"] + +[object."x:DnsServerEdgeDns"] +default = "none" +[object."x:DnsServerEdgeDns".properties] +accessToken = ["credential"] +clientSecret = ["credential"] + +[object."x:DnsServerExoscale"] +default = "none" +[object."x:DnsServerExoscale".properties] +secret = ["credential"] + +[object."x:DnsServerGlesys"] +default = "none" +[object."x:DnsServerGlesys".properties] +apiKey = ["credential"] + +[object."x:DnsServerGodaddy"] +default = "none" +[object."x:DnsServerGodaddy".properties] +secret = ["credential"] + +[object."x:DnsServerGoogleCloudDns"] +default = "none" +[object."x:DnsServerGoogleCloudDns".properties] +serviceAccountJson = ["credential"] + +[object."x:DnsServerHuaweiCloud"] +default = "none" +[object."x:DnsServerHuaweiCloud".properties] +secretKey = ["credential"] + +[object."x:DnsServerHurricane"] +default = "none" + +[object."x:DnsServerIbmCloud"] +default = "none" +[object."x:DnsServerIbmCloud".properties] +apiKey = ["credential"] + +[object."x:DnsServerInfoblox"] +default = "none" +[object."x:DnsServerInfoblox".properties] +password = ["credential"] + +[object."x:DnsServerInwx"] +default = "none" +[object."x:DnsServerInwx".properties] +password = ["credential"] +sharedSecret = ["credential"] + +[object."x:DnsServerJoker"] +default = "none" + +[object."x:DnsServerLightsail"] +default = "none" +[object."x:DnsServerLightsail".properties] +secretAccessKey = ["credential"] +sessionToken = ["credential"] + +[object."x:DnsServerLuaDns"] +default = "none" +[object."x:DnsServerLuaDns".properties] +authToken = ["credential"] + +[object."x:DnsServerMythicBeasts"] +default = "none" +[object."x:DnsServerMythicBeasts".properties] +password = ["credential"] + +[object."x:DnsServerNameDotCom"] +default = "none" +[object."x:DnsServerNameDotCom".properties] +authToken = ["credential"] + +[object."x:DnsServerNamecheap"] +default = "none" +[object."x:DnsServerNamecheap".properties] +apiKey = ["credential"] + +[object."x:DnsServerNetcup"] +default = "none" +[object."x:DnsServerNetcup".properties] +password = ["credential"] + +[object."x:DnsServerNifcloud"] +default = "none" +[object."x:DnsServerNifcloud".properties] +secretKey = ["credential"] + +[object."x:DnsServerOracleCloud"] +default = "none" +[object."x:DnsServerOracleCloud".properties] +privateKeyPassword = ["credential"] +privateKeyPem = ["credential"] + +[object."x:DnsServerOvh"] +default = "none" +[object."x:DnsServerOvh".properties] +applicationSecret = ["credential"] +consumerKey = ["credential"] + +[object."x:DnsServerPlesk"] +default = "none" +[object."x:DnsServerPlesk".properties] +apiKey = ["credential"] + +[object."x:DnsServerPorkbun"] +default = "none" +[object."x:DnsServerPorkbun".properties] +secret = ["credential"] +secretApiKey = ["credential"] + +[object."x:DnsServerRoute53"] +default = "none" +[object."x:DnsServerRoute53".properties] +secretAccessKey = ["credential"] +sessionToken = ["credential"] + +[object."x:DnsServerSpaceship"] +default = "none" +[object."x:DnsServerSpaceship".properties] +secret = ["credential"] + +[object."x:DnsServerTencentCloud"] +default = "none" +[object."x:DnsServerTencentCloud".properties] +secretKey = ["credential"] +sessionToken = ["credential"] + +[object."x:DnsServerTransip"] +default = "none" +[object."x:DnsServerTransip".properties] +privateKeyPem = ["credential"] + +[object."x:DnsServerTsig"] +default = "none" +[object."x:DnsServerTsig".properties] +host = ["network"] +key = ["credential"] + +[object."x:DnsServerUltraDns"] +default = "none" +[object."x:DnsServerUltraDns".properties] +password = ["credential"] + +[object."x:DnsServerVercel"] +default = "none" +[object."x:DnsServerVercel".properties] +authToken = ["credential"] + +[object."x:DnsServerVolcengine"] +default = "none" +[object."x:DnsServerVolcengine".properties] +secretKey = ["credential"] + +[object."x:DnsServerWebSupport"] +default = "none" +[object."x:DnsServerWebSupport".properties] +secret = ["credential"] + +[object."x:DnsServerYandexCloud"] +default = "none" +[object."x:DnsServerYandexCloud".properties] +apiKey = ["credential"] + +[object."x:Domain"] +default = "none" +whose = ["holder"] +where = ["data-store"] +scope = "tenant" +retention = "object-life" +[object."x:Domain".properties] +catchAllAddress = ["identifier"] +createdAt = ["metadata"] +description = ["content"] + +[object."x:DsnReportSettings"] +default = "none" + +[object."x:ElasticSearchStore"] +default = "none" +[object."x:ElasticSearchStore".properties] +httpAuth = ["credential"] + +[object."x:Email"] +default = "none" + +[object."x:EmailAlias"] +default = "none" +whose = ["holder"] +where = ["data-store"] +scope = "tenant" +retention = "object-life" +[object."x:EmailAlias".properties] +description = ["content"] +name = ["identifier"] + +[object."x:EmailFolder"] +default = "none" + +[object."x:EncryptionSettings"] +default = "none" + +[object."x:Enterprise"] +default = "none" +[object."x:Enterprise".properties] +apiKey = ["credential"] +licenseKey = ["credential"] + +[object."x:EventTracingLevel"] +default = "none" + +[object."x:Expression"] +default = "none" + +[object."x:ExpressionMatch"] +default = "none" + +[object."x:FileStorage"] +default = "none" + +[object."x:FileSystemStore"] +default = "none" + +[object."x:FoundationDbStore"] +default = "none" + +[object."x:FtrlParameters"] +default = "none" + +[object."x:GroupAccount"] +default = "none" +whose = ["holder"] +where = ["data-store"] +scope = "tenant" +retention = "object-life" +[object."x:GroupAccount".properties] +aliases = ["identifier"] +createdAt = ["metadata"] +description = ["content"] +emailAddress = ["identifier"] +externalId = ["identifier"] +locale = ["metadata"] +name = ["identifier"] +timeZone = ["metadata"] +usedDiskQuota = ["metadata"] + +[object."x:Http"] +default = "none" + +[object."x:HttpAuthBasic"] +default = "none" +[object."x:HttpAuthBasic".properties] +secret = ["credential"] + +[object."x:HttpAuthBearer"] +default = "none" +[object."x:HttpAuthBearer".properties] +bearerToken = ["credential"] + +[object."x:HttpForm"] +default = "none" +whose = ["administrator", "correspondent"] +where = ["data-store"] +scope = "server" +retention = "object-life" +[object."x:HttpForm".properties] +defaultFromAddress = ["contact"] +defaultName = ["contact"] +deliverTo = ["contact"] + +[object."x:HttpLookup"] +default = "none" + +[object."x:HttpLookupCsv"] +default = "none" + +[object."x:HurricaneCredential"] +default = "none" +[object."x:HurricaneCredential".properties] +secret = ["credential"] + +[object."x:Imap"] +default = "none" + +[object."x:Jmap"] +default = "none" +[object."x:Jmap".properties] +webPushKey = ["credential"] + +[object."x:JokerAuthApiKey"] +default = "none" +[object."x:JokerAuthApiKey".properties] +apiKey = ["credential"] + +[object."x:JokerAuthUsernamePassword"] +default = "none" +[object."x:JokerAuthUsernamePassword".properties] +password = ["credential"] + +[object."x:KafkaCoordinator"] +default = "none" + +[object."x:LdapDirectory"] +default = "none" +[object."x:LdapDirectory".properties] +bindSecret = ["credential"] + +[object."x:Log"] +default = "none" +whose = ["correspondent", "holder", "administrator"] +where = ["log-file"] +scope = "server" +retention = "unbounded" +[object."x:Log".properties] +details = ["network", "identifier", "metadata"] +timestamp = ["metadata"] + +[object."x:MailExchanger"] +default = "none" + +[object."x:MailingList"] +default = "none" +whose = ["holder", "correspondent"] +where = ["data-store"] +scope = "tenant" +retention = "object-life" +[object."x:MailingList".properties] +aliases = ["identifier"] +description = ["content"] +emailAddress = ["identifier"] +name = ["identifier"] +recipients = ["identifier"] + +[object."x:MaskedEmail"] +default = "none" +whose = ["holder"] +where = ["data-store"] +scope = "tenant" +retention = "object-life" +[object."x:MaskedEmail".properties] +createdAt = ["metadata"] +createdBy = ["identifier"] +description = ["content"] +email = ["identifier"] +emailDomain = ["identifier"] +emailPrefix = ["identifier"] +expiresAt = ["metadata"] +forDomain = ["content"] +url = ["content"] + +[object."x:MeilisearchStore"] +default = "none" +[object."x:MeilisearchStore".properties] +httpAuth = ["credential"] + +[object."x:MemoryLookupKey"] +default = "none" +whose = ["correspondent"] +where = ["data-store"] +scope = "server" +retention = "object-life" +[object."x:MemoryLookupKey".properties] +key = ["identifier"] + +[object."x:MemoryLookupKeyValue"] +default = "none" +whose = ["correspondent"] +where = ["data-store"] +scope = "server" +retention = "object-life" +[object."x:MemoryLookupKeyValue".properties] +key = ["identifier"] +value = ["identifier"] + +[object."x:MetricCount"] +default = "none" + +[object."x:MetricSum"] +default = "none" + +[object."x:Metrics"] +default = "none" + +[object."x:MetricsOtelGrpc"] +default = "none" + +[object."x:MetricsOtelHttp"] +default = "none" +[object."x:MetricsOtelHttp".properties] +httpAuth = ["credential"] + +[object."x:MetricsPrometheusProperties"] +default = "none" +[object."x:MetricsPrometheusProperties".properties] +authSecret = ["credential"] + +[object."x:MtaConnectionIpHost"] +default = "none" +[object."x:MtaConnectionIpHost".properties] +sourceIp = ["network"] + +[object."x:MtaConnectionStrategy"] +default = "none" + +[object."x:MtaDeliveryExpirationAttempts"] +default = "none" + +[object."x:MtaDeliveryExpirationTtl"] +default = "none" + +[object."x:MtaDeliverySchedule"] +default = "none" + +[object."x:MtaDeliveryScheduleInterval"] +default = "none" + +[object."x:MtaDeliveryScheduleIntervals"] +default = "none" + +[object."x:MtaExtensions"] +default = "none" + +[object."x:MtaHook"] +default = "none" +[object."x:MtaHook".properties] +httpAuth = ["credential"] + +[object."x:MtaInboundSession"] +default = "none" + +[object."x:MtaInboundThrottle"] +default = "none" + +[object."x:MtaMilter"] +default = "none" + +[object."x:MtaOutboundStrategy"] +default = "none" + +[object."x:MtaOutboundThrottle"] +default = "none" + +[object."x:MtaQueueQuota"] +default = "none" + +[object."x:MtaRouteCommon"] +default = "none" + +[object."x:MtaRouteMx"] +default = "none" + +[object."x:MtaRouteRelay"] +default = "none" +[object."x:MtaRouteRelay".properties] +authSecret = ["credential"] + +[object."x:MtaStageAuth"] +default = "none" + +[object."x:MtaStageConnect"] +default = "none" + +[object."x:MtaStageData"] +default = "none" + +[object."x:MtaStageEhlo"] +default = "none" + +[object."x:MtaStageMail"] +default = "none" + +[object."x:MtaStageRcpt"] +default = "none" + +[object."x:MtaSts"] +default = "none" + +[object."x:MtaTlsStrategy"] +default = "none" + +[object."x:MtaVirtualQueue"] +default = "none" + +[object."x:MySqlSettings"] +default = "none" +[object."x:MySqlSettings".properties] +authSecret = ["credential"] + +[object."x:MySqlStore"] +default = "none" +[object."x:MySqlStore".properties] +authSecret = ["credential"] + +[object."x:NatsCoordinator"] +default = "none" +[object."x:NatsCoordinator".properties] +authSecret = ["credential"] +credentials = ["credential"] + +[object."x:NetworkListener"] +default = "none" +[object."x:NetworkListener".properties] +overrideProxyTrustedNetworks = ["network"] + +[object."x:OAuthClient"] +default = "none" +whose = ["administrator"] +where = ["data-store"] +scope = "tenant" +retention = { setting = "x:OAuthClient.expiresAt" } +[object."x:OAuthClient".properties] +contacts = ["contact"] +createdAt = ["metadata"] +description = ["content"] +expiresAt = ["metadata"] +redirectUris = ["identifier"] +secret = ["credential"] + +[object."x:OidcDirectory"] +default = "none" + +[object."x:OidcProvider"] +default = "none" +[object."x:OidcProvider".properties] +encryptionKey = ["credential"] +signatureKey = ["credential"] + +[object."x:OtpAuth"] +default = "none" +whose = ["holder"] +where = ["data-store"] +scope = "tenant" +retention = "object-life" +[object."x:OtpAuth".properties] +otpCode = ["credential"] +otpUrl = ["credential"] + +[object."x:PasswordCredential"] +default = "none" +whose = ["holder"] +where = ["data-store"] +scope = "tenant" +retention = "object-life" +[object."x:PasswordCredential".properties] +allowedIps = ["network"] +expiresAt = ["metadata"] +otpAuth = ["credential"] +secret = ["credential"] + +[object."x:PermissionsList"] +default = "none" + +[object."x:PostgreSqlSettings"] +default = "none" +[object."x:PostgreSqlSettings".properties] +authSecret = ["credential"] + +[object."x:PostgreSqlStore"] +default = "none" +[object."x:PostgreSqlStore".properties] +authSecret = ["credential"] + +[object."x:PublicKey"] +default = "none" +whose = ["holder"] +where = ["data-store"] +scope = "tenant" +retention = "object-life" +[object."x:PublicKey".properties] +createdAt = ["metadata"] +description = ["content"] +emailAddresses = ["identifier"] +expiresAt = ["metadata"] +key = ["credential"] + +[object."x:PublicStringValue"] +default = "none" + +[object."x:PublicTextValue"] +default = "none" + +[object."x:QueueExpiryAttempts"] +default = "none" + +[object."x:QueueExpiryTtl"] +default = "none" + +[object."x:QueuedMessage"] +default = "none" +whose = ["holder", "correspondent"] +where = ["data-store", "blob-store"] +scope = "server" +retention = { setting = "x:MtaDeliveryExpirationTtl.expire" } +[object."x:QueuedMessage".properties] +blobId = ["content"] +createdAt = ["metadata"] +envId = ["metadata"] +receivedFromIp = ["network"] +recipients = ["identifier"] +returnPath = ["identifier"] +size = ["metadata"] + +[object."x:QueuedRecipient"] +default = "none" +whose = ["correspondent"] +where = ["data-store"] +scope = "server" +retention = { setting = "x:MtaDeliveryExpirationTtl.expire" } +[object."x:QueuedRecipient".properties] +orcpt = ["identifier"] +status = ["metadata"] + +[object."x:Rate"] +default = "none" + +[object."x:RedisClusterStore"] +default = "none" +[object."x:RedisClusterStore".properties] +authSecret = ["credential"] + +[object."x:RedisSentinelStore"] +default = "none" +[object."x:RedisSentinelStore".properties] +authSecret = ["credential"] +sentinelSecret = ["credential"] + +[object."x:RedisStore"] +default = "none" + +[object."x:ReportSettings"] +default = "none" + +[object."x:RocksDbStore"] +default = "none" + +[object."x:Role"] +default = "none" + +[object."x:S3Store"] +default = "none" +[object."x:S3Store".properties] +secretKey = ["credential"] +securityToken = ["credential"] +sessionToken = ["credential"] + +[object."x:S3StoreCustomRegion"] +default = "none" + +[object."x:Search"] +default = "none" + +[object."x:SecondaryCredential"] +default = "none" +whose = ["holder"] +where = ["data-store"] +scope = "tenant" +retention = "object-life" +[object."x:SecondaryCredential".properties] +allowedIps = ["network"] +createdAt = ["metadata"] +description = ["content"] +expiresAt = ["metadata"] +secret = ["credential"] + +[object."x:SecretKeyEnvironmentVariable"] +default = "none" + +[object."x:SecretKeyFile"] +default = "none" + +[object."x:SecretKeyValue"] +default = "none" +[object."x:SecretKeyValue".properties] +secret = ["credential"] + +[object."x:SecretTextValue"] +default = "none" +[object."x:SecretTextValue".properties] +secret = ["credential"] + +[object."x:Security"] +default = "none" + +[object."x:SenderAuth"] +default = "none" + +[object."x:ServerResponse"] +default = "none" + +[object."x:Service"] +default = "none" + +[object."x:ShardedBlobStore"] +default = "none" + +[object."x:ShardedInMemoryStore"] +default = "none" + +[object."x:Sharing"] +default = "none" + +[object."x:SieveSystemInterpreter"] +default = "none" + +[object."x:SieveSystemScript"] +default = "none" + +[object."x:SieveUserInterpreter"] +default = "none" + +[object."x:SieveUserScript"] +default = "none" +whose = ["holder"] +where = ["data-store", "blob-store"] +scope = "tenant" +retention = "object-life" +[object."x:SieveUserScript".properties] +contents = ["content"] +description = ["content"] +name = ["content"] + +[object."x:SpamClassifier"] +default = "none" + +[object."x:SpamClassifierFtrlCcfh"] +default = "none" + +[object."x:SpamClassifierFtrlFh"] +default = "none" + +[object."x:SpamClassify"] +default = "none" +whose = ["correspondent"] +where = ["memory"] +scope = "server" +retention = "unbounded" +[object."x:SpamClassify".properties] +authenticatedAs = ["identifier"] +ehloDomain = ["network"] +envFrom = ["identifier"] +envRcptTo = ["identifier"] +message = ["content"] +remoteIp = ["network"] + +[object."x:SpamClassifyTag"] +default = "none" + +[object."x:SpamDnsblServerAny"] +default = "none" + +[object."x:SpamDnsblServerBody"] +default = "none" + +[object."x:SpamDnsblServerDomain"] +default = "none" + +[object."x:SpamDnsblServerEmail"] +default = "none" + +[object."x:SpamDnsblServerHeader"] +default = "none" + +[object."x:SpamDnsblServerIp"] +default = "none" + +[object."x:SpamDnsblServerUrl"] +default = "none" + +[object."x:SpamDnsblSettings"] +default = "none" + +[object."x:SpamFileExtension"] +default = "none" + +[object."x:SpamLlmProperties"] +default = "none" + +[object."x:SpamPyzor"] +default = "none" + +[object."x:SpamRuleAny"] +default = "none" + +[object."x:SpamRuleBody"] +default = "none" + +[object."x:SpamRuleDomain"] +default = "none" + +[object."x:SpamRuleEmail"] +default = "none" + +[object."x:SpamRuleHeader"] +default = "none" + +[object."x:SpamRuleIp"] +default = "none" + +[object."x:SpamRuleUrl"] +default = "none" + +[object."x:SpamSettings"] +default = "none" + +[object."x:SpamTagAction"] +default = "none" + +[object."x:SpamTagScore"] +default = "none" + +[object."x:SpamTrainingSample"] +default = "none" +whose = ["correspondent", "holder"] +where = ["data-store", "blob-store"] +scope = "tenant" +retention = { setting = "x:SpamClassifier.holdSamplesFor" } +[object."x:SpamTrainingSample".properties] +blobId = ["content"] +expiresAt = ["metadata"] +from = ["identifier"] +subject = ["content"] + +[object."x:SpfReportSettings"] +default = "none" + +[object."x:SqlDirectory"] +default = "none" + +[object."x:SqliteStore"] +default = "none" + +[object."x:StoreLookup"] +default = "none" + +[object."x:SubAddressingCustom"] +default = "none" + +[object."x:SystemSettings"] +default = "none" +[object."x:SystemSettings".properties] +proxyTrustedNetworks = ["network"] + +[object."x:TaskAccountMaintenance"] +default = "none" + +[object."x:TaskCalendarAlarmEmail"] +default = "none" + +[object."x:TaskCalendarAlarmNotification"] +default = "none" + +[object."x:TaskCalendarItipContents"] +default = "none" +whose = ["holder", "correspondent"] +where = ["data-store"] +scope = "tenant" +retention = "unbounded" +[object."x:TaskCalendarItipContents".properties] +from = ["identifier"] +iCalendarData = ["content"] +summary = ["content"] +to = ["identifier"] + +[object."x:TaskCalendarItipMessage"] +default = "none" + +[object."x:TaskDestroyAccount"] +default = "none" +whose = ["holder"] +where = ["data-store"] +scope = "tenant" +retention = "unbounded" +[object."x:TaskDestroyAccount".properties] +accountName = ["identifier"] + +[object."x:TaskDmarcReport"] +default = "none" + +[object."x:TaskDnsManagement"] +default = "none" + +[object."x:TaskDomainManagement"] +default = "none" + +[object."x:TaskIndexDocument"] +default = "none" + +[object."x:TaskIndexTrace"] +default = "none" + +[object."x:TaskManager"] +default = "none" + +[object."x:TaskMergeThreads"] +default = "none" +whose = ["holder"] +where = ["data-store"] +scope = "tenant" +retention = "unbounded" +[object."x:TaskMergeThreads".properties] +messageIds = ["metadata"] +threadName = ["content"] + +[object."x:TaskRestoreArchivedItem"] +default = "none" + +[object."x:TaskRetryStrategyBackoff"] +default = "none" + +[object."x:TaskRetryStrategyFixed"] +default = "none" + +[object."x:TaskSpamFilterMaintenance"] +default = "none" + +[object."x:TaskStatusFailed"] +default = "none" +whose = ["holder"] +where = ["data-store"] +scope = "tenant" +retention = "unbounded" +[object."x:TaskStatusFailed".properties] +failureReason = ["content"] + +[object."x:TaskStatusPending"] +default = "none" + +[object."x:TaskStatusRetry"] +default = "none" +whose = ["holder"] +where = ["data-store"] +scope = "tenant" +retention = "unbounded" +[object."x:TaskStatusRetry".properties] +failureReason = ["content"] + +[object."x:TaskStoreMaintenance"] +default = "none" + +[object."x:TaskTenantMaintenance"] +default = "none" + +[object."x:TaskTlsReport"] +default = "none" + +[object."x:Tenant"] +default = "none" +whose = ["holder"] +where = ["data-store"] +scope = "tenant" +retention = "object-life" +[object."x:Tenant".properties] +createdAt = ["metadata"] +name = ["identifier"] +usedDiskQuota = ["metadata"] + +[object."x:TlsExternalReport"] +default = "none" +whose = ["correspondent"] +where = ["data-store"] +scope = "tenant" +retention = { setting = "x:DataRetention.holdMtaReportsFor" } +[object."x:TlsExternalReport".properties] +from = ["identifier"] +receivedAt = ["metadata"] +subject = ["content"] +to = ["identifier"] + +[object."x:TlsFailureDetails"] +default = "none" +whose = ["correspondent"] +where = ["data-store"] +scope = "tenant" +retention = { setting = "x:DataRetention.holdMtaReportsFor" } +[object."x:TlsFailureDetails".properties] +receivingIp = ["network"] +receivingMxHelo = ["network"] +receivingMxHostname = ["network"] +sendingMtaIp = ["network"] + +[object."x:TlsInternalReport"] +default = "none" +whose = ["correspondent"] +where = ["data-store"] +scope = "server" +retention = "receiver" +[object."x:TlsInternalReport".properties] +domain = ["identifier"] +httpRua = ["identifier"] +mailRua = ["identifier"] + +[object."x:TlsReport"] +default = "none" +whose = ["correspondent"] +where = ["data-store"] +scope = "tenant" +retention = { setting = "x:DataRetention.holdMtaReportsFor" } +[object."x:TlsReport".properties] +contactInfo = ["contact"] +organizationName = ["identifier"] + +[object."x:TlsReportPolicy"] +default = "none" + +[object."x:TlsReportSettings"] +default = "none" + +[object."x:Trace"] +default = "none" +whose = ["correspondent", "holder"] +where = ["data-store", "search-store"] +scope = "server" +retention = { setting = "x:DataRetention.holdTracesFor" } +[object."x:Trace".properties] +events = ["network", "identifier", "metadata"] +from = ["identifier"] +size = ["metadata"] +timestamp = ["metadata"] +to = ["identifier"] + +[object."x:TraceEvent"] +default = "none" +whose = ["correspondent", "holder"] +where = ["data-store"] +scope = "server" +retention = { setting = "x:DataRetention.holdTracesFor" } +[object."x:TraceEvent".properties] +keyValues = ["network", "identifier", "metadata"] +timestamp = ["metadata"] + +[object."x:TraceKeyValue"] +default = "none" +whose = ["correspondent", "holder"] +where = ["data-store"] +scope = "server" +retention = { setting = "x:DataRetention.holdTracesFor" } +[object."x:TraceKeyValue".properties] +value = ["network", "identifier", "metadata"] + +[object."x:TraceValueBoolean"] +default = "none" + +[object."x:TraceValueDuration"] +default = "none" + +[object."x:TraceValueEvent"] +default = "none" + +[object."x:TraceValueFloat"] +default = "none" + +[object."x:TraceValueInteger"] +default = "none" + +[object."x:TraceValueIpAddr"] +default = "none" +whose = ["correspondent", "holder"] +where = ["data-store"] +scope = "server" +retention = { setting = "x:DataRetention.holdTracesFor" } +[object."x:TraceValueIpAddr".properties] +value = ["network"] + +[object."x:TraceValueList"] +default = "none" + +[object."x:TraceValueString"] +default = "none" +whose = ["correspondent", "holder"] +where = ["data-store"] +scope = "server" +retention = { setting = "x:DataRetention.holdTracesFor" } +[object."x:TraceValueString".properties] +value = ["identifier", "metadata"] + +[object."x:TraceValueUTCDateTime"] +default = "none" + +[object."x:TraceValueUnsignedInt"] +default = "none" + +[object."x:TracerCommon"] +default = "none" + +[object."x:TracerLog"] +default = "none" +whose = ["correspondent", "holder", "administrator"] +where = ["log-file"] +scope = "server" +retention = "unbounded" +[object."x:TracerLog".properties] +path = ["metadata"] + +[object."x:TracerOtelGrpc"] +default = "none" +whose = ["correspondent", "holder", "administrator"] +where = ["external"] +scope = "server" +retention = "receiver" +[object."x:TracerOtelGrpc".properties] +endpoint = ["network"] +httpAuth = ["credential"] +httpHeaders = ["credential"] + +[object."x:TracerOtelHttp"] +default = "none" +whose = ["correspondent", "holder", "administrator"] +where = ["external"] +scope = "server" +retention = "receiver" +[object."x:TracerOtelHttp".properties] +endpoint = ["network"] +httpAuth = ["credential"] +httpHeaders = ["credential"] + +[object."x:TracerStdout"] +default = "none" + +[object."x:UserAccount"] +default = "none" +whose = ["holder"] +where = ["data-store"] +scope = "tenant" +retention = "object-life" +[object."x:UserAccount".properties] +aliases = ["identifier"] +createdAt = ["metadata"] +credentials = ["credential", "network"] +description = ["content"] +emailAddress = ["identifier"] +externalId = ["identifier"] +locale = ["metadata"] +memberGroupIds = ["identifier"] +name = ["identifier"] +timeZone = ["metadata"] +usedDiskQuota = ["metadata"] + +[object."x:WebDav"] +default = "none" + +[object."x:WebHook"] +default = "none" +whose = ["correspondent", "holder", "administrator"] +where = ["external"] +scope = "server" +retention = "receiver" +[object."x:WebHook".properties] +httpAuth = ["credential"] +httpHeaders = ["credential"] +signatureKey = ["credential"] +url = ["network"] + +[object."x:ZenohCoordinator"] +default = "none" diff --git a/tools/fork/README.md b/tools/fork/README.md index e20d37d..bbb6362 100644 --- a/tools/fork/README.md +++ b/tools/fork/README.md @@ -68,6 +68,25 @@ tools/fork/notice-check.py --fix # add it where it's missing Run `--fix` after resolving an upstream merge: a conflict resolved by taking upstream's side can drop a notice the file had. +## privacy-check.py + +Fails when the personal-data catalog (`resources/privacy/catalog.toml`) and +the code disagree: an object in the schema or one of inbuxa's own JMAP +objects with no entry, a property the schema types as an address, an IP or +a secret left to its object's default, or an entry naming an object, +property, setting or code path that no longer exists. CI runs it beside the +name check, with its tests (`python3 -m unittest discover -s tools/fork/tests`). +See `docs/spec/features/personal-data-catalog.md`. + +```bash +tools/fork/privacy-check.py # exit 1 on any finding +tools/fork/privacy-check.py --unlisted # starting entries for what's missing +``` + +After an upstream import, the strip report lists what is new and unclassified +under "Unclassified in the privacy catalog". `--unlisted` types each from the +schema alone; read the field's description before trusting it. + ## record-compat.py Records what the `*_compat` tests compare against, from the Enterprise diff --git a/tools/fork/privacy-check.py b/tools/fork/privacy-check.py new file mode 100755 index 0000000..312d3a6 --- /dev/null +++ b/tools/fork/privacy-check.py @@ -0,0 +1,228 @@ +#!/usr/bin/env python3 +# SPDX-FileCopyrightText: 2026 Coffey Labs +# SPDX-License-Identifier: AGPL-3.0-only +""" +Fail when the personal-data catalog and the code disagree. + + tools/fork/privacy-check.py # check; exit 1 on any finding + tools/fork/privacy-check.py --unlisted # print catalog entries for what's missing + +The catalog (`resources/privacy/catalog.toml`, spec +`docs/spec/features/personal-data-catalog.md`) says, for every object and +source, what personal data it can hold. An upstream import can bring objects +and fields nobody has classified, and a refactor can leave the catalog naming +things that are gone; either way the catalog stops being true without anyone +noticing, so this runs in CI on every push and pull request. It fails when: + + 1. an object in the schema's `fields`, or one of inbuxa's own JMAP objects, + has no catalog entry; + 2. a property the schema types as an email address, an IP address or + network, or a secret is covered only by its object's `default` -- it + must be listed, so a new personal field can't hide behind a default; + 3. an entry names an object, property, setting or code path that doesn't + exist (stale); + 4. an entry uses a word outside the catalog's own vocabulary. + +When it fails on a new object or field, classify it: `--unlisted` prints a +starting entry for each, typed from the schema alone. Read the property's +description before trusting it. +""" +import argparse +import gzip +import json +import os +import re +import sys +import tomllib + +ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +SCHEMA = 'resources/schema/schema.json.gz' +CATALOG = 'resources/privacy/catalog.toml' +OBJECTS_DIR = 'crates/jmap-proto/src/object' +METHODS = 'crates/jmap-proto/src/request/method.rs' + +SENSITIVE_FORMATS = { + 'emailAddress': 'identifier', + 'ipAddress': 'network', + 'ipNetwork': 'network', + 'secret': 'credential', + 'secretText': 'credential', +} +INBUXA_OBJECT = re.compile(r'"(inbuxa:[A-Z][A-Za-z]*)"') +PROPERTY_NAME = re.compile(r'=> "([a-z][A-Za-z0-9]*)"') + + +def sensitive(type_): + """The category a property's schema type alone implies, or None.""" + fmt = type_.get('format') or (type_.get('class') or {}).get('format') + if fmt in SENSITIVE_FORMATS: + return SENSITIVE_FORMATS[fmt] + name = type_.get('objectName') or (type_.get('class') or {}).get('objectName') or '' + if name.startswith(('x:SecretKey', 'x:SecretText')) or name == 'x:HttpAuth': + return 'credential' + return None + + +def load_schema(root): + with gzip.open(os.path.join(root, SCHEMA)) as f: + return json.load(f)['fields'] + + +def load_inbuxa_objects(root): + """inbuxa's own objects, from the method names jmap-proto parses.""" + with open(os.path.join(root, METHODS), encoding='utf-8') as f: + return set(INBUXA_OBJECT.findall(f.read())) + + +def inbuxa_properties(root, file_name): + """The property names a jmap-proto object file maps, or None if it's gone.""" + path = os.path.join(root, OBJECTS_DIR, file_name) + if not os.path.isfile(path): + return None + with open(path, encoding='utf-8') as f: + return set(PROPERTY_NAME.findall(f.read())) + + +def findings(root, catalog): + """Everything wrong with `catalog` against the tree at `root`.""" + fields = load_schema(root) + inbuxa = load_inbuxa_objects(root) + vocab = catalog.get('vocabulary', {}) + objects = catalog.get('object', {}) + sources = catalog.get('source', {}) + out = [] + inbuxa_props = {} + + def vocabulary(where, key, values): + allowed = set(vocab.get(key, [])) + for value in values if isinstance(values, list) else [values]: + if value not in allowed: + out.append(f'{where}: "{value}" is not in vocabulary.{key}') + + def setting_exists(where, setting): + obj, _, prop = setting.partition('.') + if obj in fields: + if prop not in fields[obj].get('properties', {}): + out.append(f'{where}: setting {setting} names no property of {obj}') + elif obj in inbuxa: + props = inbuxa_props.get(obj) + if props is not None and prop not in props: + out.append(f'{where}: setting {setting} names no property of {obj}') + else: + out.append(f'{where}: setting {setting} names no object') + + def common(where, entry): + for key in ('whose', 'where', 'scope'): + if key in entry: + vocabulary(where, key, entry[key]) + retention = entry.get('retention') + if isinstance(retention, dict): + setting_exists(where, retention.get('setting', '')) + elif retention is not None: + vocabulary(where, 'retention', retention) + for key in ('enabled_by', 'captures'): + for setting in entry.get(key, []): + setting_exists(where, setting) + + # inbuxa objects' properties first, so settings can name them + for name, entry in objects.items(): + if name.startswith('inbuxa:'): + inbuxa_props[name] = inbuxa_properties(root, entry.get('file', '')) + + # 1: nothing unlisted + for name in sorted(fields): + if name not in objects: + out.append(f'{name}: schema object has no catalog entry') + for name in sorted(inbuxa): + if name not in objects: + out.append(f'{name}: inbuxa object has no catalog entry') + + for name, entry in sorted(objects.items()): + props = entry.get('properties', {}) + if name.startswith('inbuxa:'): + known = inbuxa_props.get(name) + if name not in inbuxa: + out.append(f'{name}: no such inbuxa object (stale)') + if known is None: + out.append(f'{name}: file "{entry.get("file", "")}" not found in {OBJECTS_DIR}') + known = set() + elif name in fields: + known = set(fields[name].get('properties', {})) + else: + out.append(f'{name}: no such schema object (stale)') + continue + if entry.get('default') != 'none': + out.append(f'{name}: default must be "none"') + for prop, categories in props.items(): + if prop not in known: + out.append(f'{name}.{prop}: no such property (stale)') + vocabulary(f'{name}.{prop}', 'categories', categories) + common(name, entry) + # 2: typed-sensitive properties are listed + if name in fields: + for prop, spec in fields[name].get('properties', {}).items(): + if prop not in props and sensitive(spec['type']): + out.append(f'{name}.{prop}: typed as {sensitive(spec["type"])} but not listed') + + for name, entry in sorted(sources.items()): + where = f'source "{name}"' + vocabulary(where, 'categories', entry.get('categories', [])) + common(where, entry) + if 'leaves_host' not in entry: + out.append(f'{where}: leaves_host is missing') + for path in entry.get('written_by', []): + if not os.path.exists(os.path.join(root, path)): + out.append(f'{where}: written_by {path} doesn\'t exist (stale)') + return out + + +def unlisted(root, catalog): + """Starting catalog entries for unlisted objects and properties.""" + fields = load_schema(root) + objects = catalog.get('object', {}) + lines = [] + for name in sorted(fields): + entry = objects.get(name) + listed = (entry or {}).get('properties', {}) + missing = { + p: sensitive(spec['type']) + for p, spec in fields[name].get('properties', {}).items() + if p not in listed and sensitive(spec['type']) + } + if entry is None or missing: + lines.append(f'[object.{json.dumps(name)}]' if entry is None else f'# add to {name}:') + if entry is None: + lines.append('default = "none"') + if missing: + if entry is None: + lines.append(f'[object.{json.dumps(name)}.properties]') + for prop, category in sorted(missing.items()): + lines.append(f'{prop} = ["{category}"]') + lines.append('') + return lines + + +def main(argv=None): + parser = argparse.ArgumentParser(description=__doc__.split('\n')[1]) + parser.add_argument('--unlisted', action='store_true', help='print entries for what is missing') + parser.add_argument('--root', default=ROOT, help=argparse.SUPPRESS) + args = parser.parse_args(argv) + with open(os.path.join(args.root, CATALOG), 'rb') as f: + catalog = tomllib.load(f) + if args.unlisted: + print('\n'.join(unlisted(args.root, catalog))) + return 0 + found = findings(args.root, catalog) + if found: + print(f'privacy check: {len(found)} finding(s) in {CATALOG}:') + for line in found: + print(f' {line}') + print('Classify new objects and fields (--unlisted helps); remove what no longer exists.') + return 1 + print(f'privacy check: clean ({len(catalog.get("object", {}))} objects, ' + f'{len(catalog.get("source", {}))} sources).') + return 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/tools/fork/strip.py b/tools/fork/strip.py index 575f9cc..dad168a 100755 --- a/tools/fork/strip.py +++ b/tools/fork/strip.py @@ -373,6 +373,39 @@ def schema_flags(tree): return {'objects': objects, 'fields': fields} +def privacy_flags(tree): + """Objects and properties new in this import that the personal-data + catalog doesn't classify (docs/spec/features/personal-data-catalog.md). + Informational, like the Enterprise flags: privacy-check.py is what fails + CI once the import is merged.""" + import importlib.util + import tomllib + here = Path(__file__).resolve().parent + catalog_path = here.parents[1] / 'resources' / 'privacy' / 'catalog.toml' + current_path = here.parents[1] / 'resources' / 'schema' / 'schema.json.gz' + path = tree / 'resources' / 'schema' / 'schema.json.gz' + if not (path.is_file() and catalog_path.is_file() and current_path.is_file()): + return None + spec = importlib.util.spec_from_file_location('privacy_check', here / 'privacy-check.py') + check = importlib.util.module_from_spec(spec) + spec.loader.exec_module(check) + catalog = tomllib.loads(catalog_path.read_text(encoding='utf-8')).get('object', {}) + current = json.loads(gzip.decompress(current_path.read_bytes())).get('fields', {}) + upstream = json.loads(gzip.decompress(path.read_bytes())).get('fields', {}) + objects = sorted(name for name in upstream if name not in catalog) + fields = [] + for name, spec_ in upstream.items(): + if name not in catalog: + continue + listed = catalog[name].get('properties', {}) + known = current.get(name, {}).get('properties', {}) + for prop, p in spec_.get('properties', {}).items(): + if prop not in known and prop not in listed: + typed = check.sensitive(p.get('type', {})) + fields.append(f'{name}.{prop}' + (f' ({typed})' if typed else '')) + return {'objects': objects, 'fields': sorted(fields)} + + def remaining_hooks(tree): gates, checks = {}, {} for path in tree.rglob('*.rs'): @@ -456,6 +489,8 @@ def write_report(out_dir, report): ] if r['schema']: md.append(f'- Upstream schema flags {len(r["schema"]["objects"])} objects and {len(r["schema"]["fields"])} fields as Enterprise') + if r.get('privacy'): + md.append(f'- Unclassified in the privacy catalog: {len(r["privacy"]["objects"])} objects and {len(r["privacy"]["fields"])} new fields') md += ['', '## Removed files', ''] + [f'- `{f}`' for f in r['removed_files']] md += ['', '## Removed snippets', ''] + [f'- `{f}`: {n}' for f, n in r['removed_snippets'].items()] md += ['', '## Dangling module declarations removed', ''] + [f'- `{d["file"]}:{d["line"]}`: `mod {d["module"]}` ({" / ".join(d["lines"])})' for d in r['dangling_mods']] @@ -463,6 +498,13 @@ def write_report(out_dir, report): if r['schema']: md += ['', '## Flagged Enterprise in upstream\'s schema', '', '**Objects:** ' + ', '.join(f'`{o}`' for o in r['schema']['objects']), '', '**Fields:** ' + ', '.join(f'`{f}`' for f in r['schema']['fields'])] + if r.get('privacy'): + md += ['', '## Unclassified in the privacy catalog', '', + 'New in this import and not in `resources/privacy/catalog.toml`. `tools/fork/privacy-check.py` ' + 'fails CI on these once merged; `--unlisted` prints a starting entry. A type in brackets is what ' + 'the schema alone says the field holds.', '', + '**Objects:** ' + (', '.join(f'`{o}`' for o in r['privacy']['objects']) or 'none'), + '', '**Fields:** ' + (', '.join(f'`{f}`' for f in r['privacy']['fields']) or 'none')] md += ['', '## Third-party code', '', 'Comments in the stripped tree that name another copyright holder, another license, or a source the ' 'code came from. Files marked **new** aren\'t in THIRD-PARTY.md yet.', ''] @@ -537,7 +579,7 @@ def main(): 'removed_files': removed_files, 'removed_snippets': removed_snippets, 'cargo_edits': edits, 'dangling_mods': dangling, 'problems': problems, 'feature_gates': gates, 'edition_checks': checks, - 'schema': schema_flags(tree), 'third_party': others, 'third_party_unlisted': new_others, + 'schema': schema_flags(tree), 'privacy': privacy_flags(tree), 'third_party': others, 'third_party_unlisted': new_others, 'renames': renames, 'build': build, 'ossify_log': log, } write_report(args.out, report) diff --git a/tools/fork/tests/test_privacy_check.py b/tools/fork/tests/test_privacy_check.py new file mode 100644 index 0000000..94a2aa3 --- /dev/null +++ b/tools/fork/tests/test_privacy_check.py @@ -0,0 +1,177 @@ +# SPDX-FileCopyrightText: 2026 Coffey Labs +# SPDX-License-Identifier: AGPL-3.0-only +"""Tests for tools/fork/privacy-check.py: python3 -m unittest discover tools/fork/tests""" +import gzip +import importlib.util +import json +import os +import tempfile +import tomllib +import unittest + +HERE = os.path.dirname(os.path.abspath(__file__)) +spec = importlib.util.spec_from_file_location('privacy_check', os.path.join(HERE, '..', 'privacy-check.py')) +check = importlib.util.module_from_spec(spec) +spec.loader.exec_module(check) + +VOCAB = """ +[vocabulary] +categories = ["identifier", "contact", "network", "content", "metadata", "credential"] +whose = ["holder", "correspondent", "administrator"] +where = ["data-store", "blob-store", "search-store", "in-memory-store", "memory", "log-file", "external"] +scope = ["tenant", "server"] +retention = ["unbounded", "object-life", "receiver"] +""" + +GOOD = VOCAB + """ +[object."x:Widget"] +default = "none" +whose = ["holder"] +where = ["data-store"] +scope = "tenant" +retention = { setting = "x:Widget.keepFor" } +[object."x:Widget".properties] +owner = ["identifier"] + +[object."inbuxa:Gadget"] +file = "inbuxa_gadget.rs" +default = "none" +[object."inbuxa:Gadget".properties] +remoteIp = ["network"] + +[source."widget-log"] +categories = ["network"] +where = ["log-file"] +scope = "server" +retention = "unbounded" +enabled_by = ["x:Widget.enable", "inbuxa:Gadget.remoteIp"] +leaves_host = false +written_by = ["crates/widget.rs"] +""" + + +def make_tree(fields): + root = tempfile.mkdtemp() + os.makedirs(os.path.join(root, 'resources/schema')) + with gzip.open(os.path.join(root, check.SCHEMA), 'wt') as f: + json.dump({'fields': fields}, f) + os.makedirs(os.path.join(root, check.OBJECTS_DIR)) + with open(os.path.join(root, check.OBJECTS_DIR, 'inbuxa_gadget.rs'), 'w') as f: + f.write('Property::RemoteIp => "remoteIp",\nProperty::Id => "id",\n') + os.makedirs(os.path.join(root, 'crates/jmap-proto/src/request'), exist_ok=True) + with open(os.path.join(root, check.METHODS), 'w') as f: + f.write('"inbuxa:Gadget" => MethodObject::Gadget,\n') + with open(os.path.join(root, 'crates/widget.rs'), 'w') as f: + f.write('') + return root + + +def prop(type_, fmt=None): + t = {'type': type_} + if fmt: + t['format'] = fmt + return {'type': t} + + +FIELDS = { + 'x:Widget': {'properties': { + 'owner': prop('string', 'emailAddress'), + 'enable': prop('boolean'), + 'keepFor': prop('number', 'duration'), + 'label': prop('string', 'string'), + }}, +} + + +class PrivacyCheck(unittest.TestCase): + def run_check(self, catalog, fields=FIELDS): + return check.findings(make_tree(fields), tomllib.loads(catalog)) + + def test_the_repository_passes(self): + with open(os.path.join(check.ROOT, check.CATALOG), 'rb') as f: + catalog = tomllib.load(f) + self.assertEqual(check.findings(check.ROOT, catalog), []) + + def test_a_consistent_catalog_passes(self): + self.assertEqual(self.run_check(GOOD), []) + + def test_an_unclassified_object_fails(self): + fields = dict(FIELDS, **{'x:Sprocket': {'properties': {'size': prop('number', 'size')}}}) + found = self.run_check(GOOD, fields) + self.assertEqual(found, ['x:Sprocket: schema object has no catalog entry']) + + def test_an_address_hidden_behind_the_default_fails(self): + fields = {'x:Widget': {'properties': dict(FIELDS['x:Widget']['properties'], + contactEmail=prop('string', 'emailAddress'))}} + found = self.run_check(GOOD, fields) + self.assertEqual(found, ['x:Widget.contactEmail: typed as identifier but not listed']) + + def test_a_secret_in_a_set_or_object_counts(self): + fields = {'x:Widget': {'properties': dict( + FIELDS['x:Widget']['properties'], + ips={'type': {'type': 'set', 'class': {'type': 'string', 'format': 'ipNetwork'}}}, + key={'type': {'type': 'object', 'objectName': 'x:SecretKey'}}, + )}} + found = self.run_check(GOOD, fields) + self.assertIn('x:Widget.ips: typed as network but not listed', found) + self.assertIn('x:Widget.key: typed as credential but not listed', found) + + def test_a_stale_property_fails(self): + found = self.run_check(GOOD.replace('owner = ["identifier"]', 'owner = ["identifier"]\ngone = ["content"]')) + self.assertEqual(found, ['x:Widget.gone: no such property (stale)']) + + def test_a_stale_object_fails(self): + found = self.run_check(GOOD + '\n[object."x:Removed"]\ndefault = "none"\n') + self.assertEqual(found, ['x:Removed: no such schema object (stale)']) + + def test_a_stale_setting_fails(self): + found = self.run_check(GOOD.replace('x:Widget.keepFor', 'x:Widget.keepForever')) + self.assertEqual(found, ['x:Widget: setting x:Widget.keepForever names no property of x:Widget']) + + def test_a_stale_code_path_fails(self): + found = self.run_check(GOOD.replace('crates/widget.rs', 'crates/gone.rs')) + self.assertEqual(found, ['source "widget-log": written_by crates/gone.rs doesn\'t exist (stale)']) + + def test_an_unlisted_inbuxa_object_fails(self): + catalog = VOCAB + """ +[object."x:Widget"] +default = "none" +[object."x:Widget".properties] +owner = ["identifier"] +""" + found = self.run_check(catalog) + self.assertEqual(found, ['inbuxa:Gadget: inbuxa object has no catalog entry']) + + def test_words_outside_the_vocabulary_fail(self): + found = self.run_check(GOOD.replace('owner = ["identifier"]', 'owner = ["personal"]')) + self.assertEqual(found, ['x:Widget.owner: "personal" is not in vocabulary.categories']) + + def test_unlisted_prints_a_starting_entry(self): + fields = dict(FIELDS, **{'x:Sprocket': {'properties': {'mail': prop('string', 'emailAddress')}}}) + lines = check.unlisted(make_tree(fields), tomllib.loads(GOOD)) + self.assertIn('[object."x:Sprocket"]', lines) + self.assertIn('mail = ["identifier"]', lines) + + +class StripReport(unittest.TestCase): + def test_an_import_reports_what_is_new_and_unclassified(self): + import pathlib + strip_spec = importlib.util.spec_from_file_location('strip', os.path.join(HERE, '..', 'strip.py')) + strip = importlib.util.module_from_spec(strip_spec) + strip_spec.loader.exec_module(strip) + with gzip.open(os.path.join(check.ROOT, check.SCHEMA)) as f: + upstream = json.load(f) + upstream['fields']['x:NewThing'] = {'properties': {}} + upstream['fields']['x:UserAccount']['properties']['backupEmail'] = prop('string', 'emailAddress') + tree = pathlib.Path(tempfile.mkdtemp()) + (tree / 'resources/schema').mkdir(parents=True) + with gzip.open(tree / 'resources/schema/schema.json.gz', 'wt') as f: + json.dump(upstream, f) + self.assertEqual(strip.privacy_flags(tree), { + 'objects': ['x:NewThing'], + 'fields': ['x:UserAccount.backupEmail (identifier)'], + }) + + +if __name__ == '__main__': + unittest.main() From a0ffdb8071525d18ba7d6d430b9c1171dc91e1e4 Mon Sep 17 00:00:00 2001 From: John Coffey Date: Mon, 28 Sep 2026 07:43:59 -0700 Subject: [PATCH 5/6] New-install privacy defaults, and expired bans purged daily Personal-data catalog spec, defaults D2, D3, D4, D6 and D7 (settled 2026-09-28, new installs only): - D2: automatic IP bans expire after 30 days instead of never; D3: spam training samples, whole messages, are kept 90 days instead of 180; D4: Pyzor, which sends a digest of each message's text to a public server, is off; D6: delivery history is kept 14 days instead of 30. Written on the first boot of a new install only -- one with no roles yet, the same test the built-in roles use -- by reading each singleton, setting these fields and writing it back whole. A server with roles keeps its settings, saved or default. - D7: a webhook created from now on starts with the include policy and no events, so it sends nothing until events are chosen (Rust default and schema default, marked). The registry stores every field, so existing webhooks keep their policy. - Expired bans are also removed by the daily data clean-up. They already stopped blocking and were deleted when settings next loaded; a server that seldom reloads kept them. D1 (log retention) and D5 (the hashed-address blocklist off) are held, and the spec says why: x:TracerLog is stored inside x:Bootstrap with a field after it, so adding one changes that object's stored format; and the spam-rules loader D5 touches is being reworked by the v0.16.24 import. The spec also corrects finding 3: expired bans were deleted on settings load; bans were permanent only because no period is set. Tested: unit tests for the new-install values and that everything else in each singleton stays; the system suite, whose security test now purges an expired ban and checks its record is gone; the telemetry test; common's unit tests; fork checks. --- crates/common/src/manager/defaults.rs | 102 +++++++++++++++++- crates/common/src/network/security.rs | 31 ++++++ crates/registry/src/schema/structs_impl.rs | 4 +- .../services/src/task_manager/maintenance.rs | 5 + docs/spec/features/personal-data-catalog.md | 19 +++- resources/schema/schema.json.gz | Bin 151262 -> 151264 bytes resources/schema/schema.json.sha256 | 2 +- tests/src/system/security.rs | 15 +++ 8 files changed, 172 insertions(+), 6 deletions(-) diff --git a/crates/common/src/manager/defaults.rs b/crates/common/src/manager/defaults.rs index 66e03f5..bd5e1d6 100644 --- a/crates/common/src/manager/defaults.rs +++ b/crates/common/src/manager/defaults.rs @@ -14,7 +14,7 @@ use aws_lc_rs::{ use registry::{ schema::{ enums::*, - prelude::{ObjectType, SocketAddr}, + prelude::{Object, ObjectType, SocketAddr}, structs::*, }, types::{duration::Duration, error::Error, list::List, map::Map}, @@ -388,6 +388,28 @@ async fn insert_safe_defaults(bp: &mut Bootstrap) -> trc::Result<()> { } } + // inbuxa: personal-data catalog, defaults D2, D3, D4 and D6 (settled + // 2026-09-28): privacy-leaning values, for new installs only. A server + // with roles is not new, and keeps its settings whether saved or left at + // the default. Each singleton is read, changed and written back whole, so + // anything already in it stays. + #[cfg(not(feature = "test_mode"))] + if bp.registry.count_object(ObjectType::Role).await? == 0 { + let mut security = bp.setting_infallible::().await; + let mut classifier = bp.setting_infallible::().await; + let mut pyzor = bp.setting_infallible::().await; + let mut retention = bp.setting_infallible::().await; + new_install_privacy_defaults(&mut security, &mut classifier, &mut pyzor, &mut retention); + for object in [ + Object::from(security), + classifier.into(), + pyzor.into(), + retention.into(), + ] { + bp.registry.write(RegistryWrite::insert(&object)).await?; + } + } + if bp.registry.count_object(ObjectType::Role).await? == 0 { let permissions = DefaultPermissions::default(); let mut role_ids = Vec::with_capacity(4); @@ -560,3 +582,81 @@ async fn insert_safe_defaults(bp: &mut Bootstrap) -> trc::Result<()> { Ok(()) } + +/// inbuxa: the new-install values of defaults D2, D3, D4 and D6 from the +/// personal-data catalog spec. Automatic IP bans expire after 30 days instead +/// of never; spam training samples are kept 90 days instead of 180; Pyzor, +/// which sends a digest of each message's text to a public server, is off; +/// delivery history is kept 14 days instead of 30. +fn new_install_privacy_defaults( + security: &mut Security, + classifier: &mut SpamClassifier, + pyzor: &mut SpamPyzor, + retention: &mut DataRetention, +) { + const DAY: u64 = 24 * 60 * 60 * 1000; + let ban_period = Some(Duration::from_millis(30 * DAY)); + security.auth_ban_period = ban_period; + security.abuse_ban_period = ban_period; + security.loiter_ban_period = ban_period; + security.scan_ban_period = ban_period; + classifier.hold_samples_for = Duration::from_millis(90 * DAY); + pyzor.enable = false; + retention.hold_traces_for = Some(Duration::from_millis(14 * DAY)); +} + +#[cfg(test)] +mod tests { + use super::*; + + const DAY: u64 = 24 * 60 * 60 * 1000; + + #[test] + fn new_installs_get_the_privacy_defaults() { + let (mut security, mut classifier, mut pyzor, mut retention) = ( + Security::default(), + SpamClassifier::default(), + SpamPyzor::default(), + DataRetention::default(), + ); + // What an install gets without them: bans that never lift, 180-day + // samples, Pyzor on, 30-day traces. + assert_eq!(security.auth_ban_period, None); + assert!(pyzor.enable); + + new_install_privacy_defaults(&mut security, &mut classifier, &mut pyzor, &mut retention); + + for period in [ + security.auth_ban_period, + security.abuse_ban_period, + security.loiter_ban_period, + security.scan_ban_period, + ] { + assert_eq!(period.map(|p| p.into_inner().as_millis() as u64), Some(30 * DAY)); + } + assert_eq!(classifier.hold_samples_for.into_inner().as_millis() as u64, 90 * DAY); + assert!(!pyzor.enable); + assert_eq!( + retention.hold_traces_for.map(|p| p.into_inner().as_millis() as u64), + Some(14 * DAY) + ); + } + + #[test] + fn everything_else_in_the_settings_stays() { + let mut retention = DataRetention { + archive_deleted_items_for: Some(Duration::from_millis(7 * DAY)), + ..Default::default() + }; + let before = retention.clone(); + new_install_privacy_defaults( + &mut Security::default(), + &mut SpamClassifier::default(), + &mut SpamPyzor::default(), + &mut retention, + ); + assert_eq!(retention.archive_deleted_items_for, before.archive_deleted_items_for); + assert_eq!(retention.hold_metrics_for, before.hold_metrics_for); + assert_eq!(retention.expunge_trash_after, before.expunge_trash_after); + } +} diff --git a/crates/common/src/network/security.rs b/crates/common/src/network/security.rs index cdbadee..cdf10df 100644 --- a/crates/common/src/network/security.rs +++ b/crates/common/src/network/security.rs @@ -426,6 +426,37 @@ impl Server { } } +impl Server { + /// inbuxa: personal-data catalog, D2: removes bans whose period is over. + /// They already stop blocking when they expire, and go when settings are + /// next loaded; the daily clean-up makes sure a server that seldom + /// reloads doesn't keep them. + pub async fn purge_expired_blocked_ips(&self) -> trc::Result<()> { + let now = now() as i64; + let mut expired = Vec::new(); + for ip in self.registry().list::().await? { + if ip.object.expires_at.as_ref().is_some_and(|at| at.timestamp() <= now) { + let address = ip.object.address.clone(); + let object = Object { + inner: ip.object.into(), + revision: ip.revision, + }; + self.registry() + .write(RegistryWrite::delete_object(ip.id, &object)) + .await?; + expired.push(trc::Value::from(address.into_inner().0)); + } + } + if !expired.is_empty() { + trc::event!( + Security(trc::SecurityEvent::IpBlockExpired), + Details = expired + ); + } + Ok(()) + } +} + impl BlockedIps { pub async fn parse(bp: &mut Bootstrap) -> Self { let mut ips = Self::default(); diff --git a/crates/registry/src/schema/structs_impl.rs b/crates/registry/src/schema/structs_impl.rs index 0b7fc8e..1ef2839 100644 --- a/crates/registry/src/schema/structs_impl.rs +++ b/crates/registry/src/schema/structs_impl.rs @@ -47353,7 +47353,9 @@ impl Default for WebHook { level: TracingLevel::Info, lossy: false, events: Default::default(), - events_policy: EventPolicy::Exclude, + // inbuxa: personal-data catalog, D7: a new webhook sends nothing + // until its events are chosen + events_policy: EventPolicy::Include, } } } diff --git a/crates/services/src/task_manager/maintenance.rs b/crates/services/src/task_manager/maintenance.rs index 9b69c2b..aa8d1d7 100644 --- a/crates/services/src/task_manager/maintenance.rs +++ b/crates/services/src/task_manager/maintenance.rs @@ -269,6 +269,11 @@ async fn store_maintenance( trc::error!(err.details("Failed to re-apply account locks")); } + // inbuxa: personal-data catalog, D2: bans past their period go + if let Err(err) = server.purge_expired_blocked_ips().await { + trc::error!(err.details("Failed to purge expired IP bans")); + } + // inbuxa: AU-7: audit records past their retention go; a // failure leaves them for the next run if let Err(err) = server.audit_purge().await { diff --git a/docs/spec/features/personal-data-catalog.md b/docs/spec/features/personal-data-catalog.md index d317279..e8416e3 100644 --- a/docs/spec/features/personal-data-catalog.md +++ b/docs/spec/features/personal-data-catalog.md @@ -151,7 +151,7 @@ These live in inbuxa's own key space (`SUBSPACE_INBUXA`) unless noted. | OAuth codes and tokens | credential | holder | `x:OidcProvider.*Expiry` | tokens are sealed and stateless (not stored); codes in the in-memory store with TTL | in-memory store | tenant | `http/src/auth/oauth/auth.rs`, `token.rs` | codes 10 min | | Rate-limit state | network, identifier (login names) | holder, correspondent | `x:Http.rateLimit*`, `x:Imap.maxRequestRate`, `x:Security.*BanRate` | the rate's period | in-memory store | server | `common/src/auth/rate_limit.rs`, `network/security.rs` | on | | Greylist | identifier (sender/recipient pairs, plain) | correspondent, holder | `x:SpamSettings.greylistFor` | that period | in-memory store | server | `smtp/src/inbound/rcpt.rs` | **off** | -| Automatic IP bans (`x:BlockedIp`) | network | correspondent, holder | `x:Security.authBanRate`, `abuseBanRate`, `loiterBanRate`, `scanBanRate` (on); `*BanPeriod` (**no default**) | **unbounded: a ban with no period never expires, and no purge of expired bans was found**; each ban is also an audit record | data store (registry) | server | `common/src/network/security.rs` `block_ip` | **collected, permanent** | +| Automatic IP bans (`x:BlockedIp`) | network | correspondent, holder | `x:Security.authBanRate`, `abuseBanRate`, `loiterBanRate`, `scanBanRate` (on); `*BanPeriod` (**no default**) | **unbounded: a ban with no period never expires**; an expired ban's record goes when settings next load; each ban is also an audit record | data store (registry) | server | `common/src/network/security.rs` `block_ip` | **collected, permanent** | | Allowed IPs | network | administrator's choice | manual; `expiresAt` | optional | data store | server | registry | none | ### 2.6 Spam filter and AI @@ -197,8 +197,10 @@ not a judgment; what to do about each is John's call. 2. **Log files are never deleted.** Daily rotation opens a new file; nothing removes old ones, and no logrotate configuration ships. At the default level every in-session line carries the client IP. -3. **Automatic IP bans are permanent.** No `*BanPeriod` has a default, and no - purge of expired `x:BlockedIp` records was found. +3. **Automatic IP bans are permanent.** No `*BanPeriod` has a default, so a + ban never expires. (Corrected 2026-09-28: a ban that does expire stops + blocking, and its record is deleted when settings are next loaded, in + `BlockedIps::parse`; the investigation missed that path.) 4. **Some records outlive the account.** Deleting an account doesn't clear inbuxa's own key space: legacy-protocol last use, account locks, masked address records and audit records stay (audit records by design). @@ -394,6 +396,17 @@ default; fails on a stale entry. Phase 3; existing servers keep their settings. D7 is also covered by the bug fix for finding 1 (Settled 6). +**As built (2026-09-28).** D2, D3, D4, D6 are written on first boot of a new +install only (no roles yet), each singleton read and written back whole +(`manager/defaults.rs`, `new_install_privacy_defaults`); expired bans are +also purged daily (`purge_expired_blocked_ips`). D7 changes the default for +webhooks created from now on; stored webhooks keep theirs (the registry +stores every field). **Held:** D1, because `x:TracerLog` is also stored +inside `x:Bootstrap` with fields after it, so adding a field changes that +object's stored format; a fork-owned setting is proposed instead, for John +to decide. D5, because the spam-rules loader it touches is being reworked +by the v0.16.24 import. + | # | Change | Trade-off | |---|---|---| | D1 | A **log retention** setting on `x:TracerLog` (delete rotated files older than N days), default 30 days for new installs | Needs code (a new field, so a schema edit); older logs gone for troubleshooting; operators wanting longer set it | diff --git a/resources/schema/schema.json.gz b/resources/schema/schema.json.gz index bc1c4ef006e69d0fb1b3abbc011c5d47e6b6b742..862bb886a355c4bbb4adbbd52623f37a3866202f 100644 GIT binary patch delta 91656 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 P;|l*5CXx+6AZZQ&O2JEW delta 91654 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 iNKFt48bSPuB4_D( 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 NU+o8Ole-{k4gjeqK)e6| diff --git a/resources/schema/schema.json.sha256 b/resources/schema/schema.json.sha256 index cf8d413..083abdc 100644 --- a/resources/schema/schema.json.sha256 +++ b/resources/schema/schema.json.sha256 @@ -1 +1 @@ --jadTddv9zRK0gp8fBFYRmhwmXopxPxF2yjc86bSKRM \ No newline at end of file +MiWRzsz0AHdRshG_8JimktRqBO_pHGAUBHFcfV5jwI4 \ No newline at end of file diff --git a/tests/src/system/security.rs b/tests/src/system/security.rs index 76a397b..c107a33 100644 --- a/tests/src/system/security.rs +++ b/tests/src/system/security.rs @@ -221,6 +221,21 @@ pub async fn test(test: &mut TestServer) { ) .await; + // inbuxa: personal-data catalog, D2: the daily clean-up removes the + // expired ban's record, without waiting for settings to reload + test.server.purge_expired_blocked_ips().await.unwrap(); + assert_eq!( + admin + .registry_query_ids( + ObjectType::BlockedIp, + [(Property::Address, "10.0.0.2")], + Vec::<&str>::new(), + ) + .await, + Vec::::new(), + "the expired ban's record is gone" + ); + // Make sure the IP remains unblocked after reload admin.registry_create_object(Action::ReloadBlockedIps).await; validate_password_with_ip( From f47371b3a15f72c8cfcd9ab549eef9325bd048e9 Mon Sep 17 00:00:00 2001 From: John Coffey Date: Mon, 28 Sep 2026 08:09:32 -0700 Subject: [PATCH 6/6] Spec: settle where log retention lives and when D5 is built D1 becomes a fork-owned setting, as audit retention is, because a field on x:TracerLog would change x:Bootstrap's stored format. D5 waits for the v0.16.24 import's reworked spam-rules loader. --- docs/spec/features/personal-data-catalog.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/spec/features/personal-data-catalog.md b/docs/spec/features/personal-data-catalog.md index e8416e3..6229d8b 100644 --- a/docs/spec/features/personal-data-catalog.md +++ b/docs/spec/features/personal-data-catalog.md @@ -401,11 +401,13 @@ install only (no roles yet), each singleton read and written back whole (`manager/defaults.rs`, `new_install_privacy_defaults`); expired bans are also purged daily (`purge_expired_blocked_ips`). D7 changes the default for webhooks created from now on; stored webhooks keep theirs (the registry -stores every field). **Held:** D1, because `x:TracerLog` is also stored -inside `x:Bootstrap` with fields after it, so adding a field changes that -object's stored format; a fork-owned setting is proposed instead, for John -to decide. D5, because the spam-rules loader it touches is being reworked -by the v0.16.24 import. +stores every field). **Settled (John, 2026-09-28):** D1 becomes a +fork-owned log-retention setting, kept in inbuxa's own storage as audit +retention is (not a field on `x:TracerLog`, which is also stored inside +`x:Bootstrap` with fields after it, so a new field would change that +object's stored format); new installs 30 days, existing servers keep every +file as today. D5 is built after the v0.16.24 import lands, on its reworked +spam-rules loader, which keeps each blocklist's on/off state. | # | Change | Trade-off | |---|---|---|