summaryrefslogtreecommitdiffstats
path: root/docs/FOG-OBSERVABILITY.md
blob: b72034ff54e44ef1f2a28612c1e8b5d8c51498a0 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
# FOG Observability

Status: Structural Baseline 0.1

Date: 2026-08-08

## 1. Purpose and Claim Boundary

`FOG-OBSERVABILITY-1` defines privacy-safe local logging, operational metric
collection, aggregate submission, operator diagnosis, and public health
publication for FOG.

The objective is to detect failed or unsafe role state without creating a
second traffic-analysis system. Observability is part of the threat model. A
log server, tracing system, dashboard, crash collector, or support workflow
can otherwise preserve exactly the timing and relationship metadata that the
data plane is intended to minimize.

This baseline fixes:

- a no-event-stream production default;
- a closed metric and local-summary vocabulary;
- coarse non-overlapping collection windows;
- bucketed values and low-activity suppression;
- delayed role-to-observer submissions;
- separate local, operator, and public views;
- public anti-differencing requirements;
- bounded local retention and raw-submission expiry;
- crash, support, debug, access, and conformance rules.

It does not activate a numeric observability profile. Window duration, bucket
boundaries, minimum activity, minimum independent reporters, release delay,
merge policy, retention, serialization, signature suite, and public grouping
remain explicit evidence-gated selections. No implementation or deployment
may silently choose them as protocol defaults.

The standard-library Go module in `../observability/` is an executable
conformance model for the role-local typed collector and bounded volatile
ring. Its test values are synthetic. It is not `fog-observer`, an IF-11 wire
codec, a signature implementation, or a public exporter.

## 2. Governing Requirements

This document refines:

- `TM-OPS-01`, logging and metrics leakage;
- `TM-NET-01`, timing and volume correlation;
- `TM-NET-03`, active suppression and n-1 behavior;
- `TM-ROLE-01` through `TM-ROLE-03`, role compromise and collusion;
- `TM-AVAIL-01`, bounded work and overload handling;
- `ARC-001`, separate trust domains;
- `ARC-007`, one owner for private state and keys;
- `ARC-008`, bounded inputs, work, queues, and state;
- `ARC-009`, no privacy-weakening recovery;
- `IF-11`, role-to-observer aggregate submission;
- the logging sections of `FOG-WIRE`, `FOG-SPHINX-PROFILES`,
  `FOG-STORAGE`, and `FOG-COMPOSER`.

When this document is narrower than a generic logging or monitoring practice,
this document controls for claim-bearing FOG profiles.

## 3. Threat Model

The protected assets are:

- whether one user, peer, mailbox, route, packet, or conversation was active;
- exact event time, direction, sequence, size, and duration;
- linkability across roles, hosts, epochs, restarts, or support cases;
- capabilities, keys, tokens, replay state, and opaque protocol objects;
- operator, host, deployment, and user metadata not already required in
  authenticated public consensus.

Relevant adversaries include a compromised role, observer, dashboard,
operator account, support system, backup, or log collector, as well as an
external party that later obtains retained operational data. A curious or
compromised observer is within scope. The design therefore does not assume
that centralizing raw logs is safe.

No observability design can hide what a node operator sees directly on that
node or what a network observer sees on links. The goal is narrower: do not
create a new persistent, cross-role, queryable correlation dataset.

## 4. Invariants

### OBS-INV-01: No data-plane event stream

Production roles do not emit one record for each packet, message, handshake,
storage request, read, write, retry, connection, or cryptographic operation.
They update bounded in-memory counters or health state inside one coarse
window.

### OBS-INV-02: Closed typed vocabulary

Every field, role, metric, state, failure class, value kind, and visibility
class is allowlisted. Production collection APIs accept no arbitrary label
map, free-form message, raw error, peer-provided string, path, address, or
identifier.

### OBS-INV-03: Coarse windows only

A role records one window identifier, not event timestamps. Windows are
profile-fixed, aligned, and non-overlapping. Operators and public consumers
cannot request arbitrary time ranges.

### OBS-INV-04: No exact exported traffic values

Raw counters exist only inside the open volatile collector. Sealing converts
them to profile-defined buckets. Exact counts, sizes, latencies, ratios, queue
depths, and durations do not enter local summaries, observer submissions, or
publications.

### OBS-INV-05: Suppress small activity sets

Below the profile's minimum activity, every traffic-sensitive metric carries
the fixed `suppressed` value. Suppression does not remove fields or change the
report shape. Non-traffic health state may remain available.

### OBS-INV-06: Delayed fixed-schedule submission

Online roles submit on a profile-fixed schedule independent of whether real
traffic occurred. Each submission declares the earliest release window.
Immediate event-triggered submission is forbidden.

### OBS-INV-07: Public output requires multiple reporters

No role-local report can become a public report. `fog-observer` first combines
the profile-required number of independently authenticated reporters, then
applies coarsening, delay, suppression, and differencing controls.

### OBS-INV-08: No cumulative or subtractable public series

Public output does not expose cumulative counters, overlapping query windows,
arbitrary filters, node-level series, or stable dimensions that allow two
answers to isolate a smaller group.

### OBS-INV-09: Bounded retention

Local summaries use a bounded volatile ring or a profile-authorized protected
store. Raw signed observer submissions expire after bounded processing.
Retention is fixed in the active profile and cannot be extended through a
dashboard query or support request.

### OBS-INV-10: Observability is not required for forwarding

Failure, absence, overload, or compromise of `fog-observer` does not alter
packet forwarding, consensus validity, storage behavior, cover scheduling, or
cryptographic validation. Roles queue at most a bounded aggregate submission
and otherwise discard it.

### OBS-INV-11: Production tracing is unavailable

Claim-bearing builds have no packet, record, request, span, or connection
tracing mode. `DEBUG` or `TRACE` cannot be enabled by runtime configuration.
Synthetic conformance builds are a separate artifact rejected by production
configuration.

### OBS-INV-12: Fixed shape within role and profile

Every local summary and observer report contains the same ordered metric
positions for its role and profile. Unset values are `unknown` or `zero`.
Sensitive low-activity values are `suppressed`. Presence or field order does
not reveal which code path ran.

## 5. Information Planes

FOG uses three observability planes:

1. **Local safety summary**: one role retains bounded coarse windows needed
   for local diagnosis. It never contains event records.
2. **Operator aggregate**: an online role sends a delayed fixed-shape
   aggregate over mutually authenticated IF-11. The observer may use it for a
   protected operator health view.
3. **Public aggregate**: the observer combines enough independent reporters
   and publishes only a delayed, coarsened, non-subtractable view.

```text
role-local raw counters, volatile within one window
                     |
                     | seal once, bucket, suppress
                     v
       bounded local summary + delayed IF-11 aggregate
                                      |
                                      | authenticate, combine, expire raw
                                      v
                       operator view          public view
                       protected              delayed and multi-reporter
```

The networkless Composer and `fog-sx-send`/`fog-sx-receive` do not submit
automatic observer reports. They may retain only the local safety summary
allowed by their deployment profile.

## 6. Data Classes

The following classes are forbidden in every FOG log, metric, report,
dashboard, crash artifact, and generic support bundle:

- plaintext, drafts, rendered content, contacts, labels, fingerprints, and
  application fields;
- packet or record bytes, ciphertext samples, entry capsules, KEMSphinx
  packets, storage envelopes, and imported attacker input;
- capabilities, vouchers, SURBs, reply material, replay tags, receipt
  material, ratchets, storage streams, and secret or private key material;
- message, session, packet, request, record, box, receipt, connection, trace,
  span, route, or bundle identifiers;
- source or destination IP address, peer ID, next-hop ID, full route, replica
  selection, entry set, or per-source history;
- exact event timestamps, per-operation duration, direction trace, retry
  sequence, queue item, read frequency, miss streak, and tombstone timing;
- raw error strings, parser offsets, expected values, detailed cryptographic
  stages, stack traces, memory dumps, or exception objects;
- hostname, username, filesystem path, locale, timezone, device serial,
  billing data, private operator contact, or infrastructure detail absent from
  the authenticated public consensus.

Hashes, truncation, encryption under a central log key, or pseudonymization do
not make a forbidden identifier safe. A stable digest remains a correlation
handle.

## 7. Production Defaults

Before a numeric observability profile is activated, the safe defaults are:

- no packet, request, connection, or storage-access log;
- no automatic remote logging or telemetry;
- no public metrics endpoint and no host-published Prometheus endpoint;
- no OpenTelemetry auto-instrumentation, trace ID, or span ID;
- no automatic crash upload or support bundle;
- core dumps, process-memory capture, and production debug mode disabled;
- bounded volatile local summary only;
- observer submission and public publication disabled;
- generic application errors reduced to local fixed failure classes;
- remote errors remain coarse and non-amplifying under the owning protocol.

A deployment is not permitted to replace these defaults with a generic log
shipper, service-mesh tracer, application performance monitor, or container
log collector and still claim the same profile.

## 8. Role-Local Window Model

One role creates exactly one collector for each coarse window. It may update:

- a fixed unsigned counter for an allowlisted count metric;
- a fixed level bucket for queue, age, latency, ratio, or capacity state;
- a fixed health value for consensus, clock, key, replay, or cover state;
- one internal activity counter used only for the suppression decision.

All counters saturate on overflow. They never wrap. The collector holds no
identifiers, timestamps, strings, samples, exemplars, or per-peer maps.

Sealing is irreversible and happens once. It produces:

- one local report for every role;
- one delayed observer report only for an online reporting role;
- no observer report for Composer, observer, or FOG-SX roles.

The role then discards its exact counters. A retry of IF-11 transmits the same
sealed aggregate object according to the future command-specific retry rule;
it does not reopen or recount the window.

Discarding collector references is best-effort data minimization. The Go
runtime, allocator, swap, hibernation, crash capture, and host may retain
copies, so deployment controls remain necessary and complete erasure is not
claimed.

## 9. Structural Report Schema

The in-process structural report contains only:

| Field | Meaning | Restriction |
| --- | --- | --- |
| `schema_version` | report schema version | fixed to version 1 |
| `profile_id` | authenticated observability profile | nonzero and consensus-authorized before activation |
| `scope` | `local` or `observer` | fixed enum |
| `role` | reporting security role | fixed enum, not node identity |
| `window_id` | coarse aligned window | no event time |
| `release_after_window` | earliest observer processing/release boundary | observer scope only and later than source window |
| `traffic_suppressed` | sensitive metrics are below threshold | fixed boolean, submission schedule remains constant |
| `measurements` | ordered fixed catalog for role and scope | metric and value enums only |

The JSON emitted by the Go conformance module is a local test and diagnostic
representation, not the IF-11 wire encoding. Claim-bearing IF-11 requires a
separate byte-exact bounded encoding and signature specification.

An aggregate-signing key identifier, signature suite, and signature belong to
the future authenticated IF-11 submission envelope. They are raw observer
input, never report measurements or public dimensions. Exact signing is not
defined until the PKI and observability suites select an encoding and
primitive.

## 10. Value Vocabulary

Count metrics use only:

- `zero`;
- `low`;
- `medium`;
- `high`;
- `saturated`;
- `suppressed` when traffic-sensitive activity is below threshold.

Level metrics use only:

- `unknown`;
- `empty`;
- `low`;
- `medium`;
- `high`;
- `full`;
- `suppressed` when traffic-sensitive activity is below threshold.

Health metrics use only:

- `unknown`;
- `healthy`;
- `degraded`;
- `unavailable`.

Bucket boundaries are part of one signed profile. Operators cannot customize
them per node. Public documentation states the boundaries and their privacy
rationale after activation.

## 11. Metric Catalog

The structural catalog is intentionally small. `Public eligible` means only
that a future observer may consider the metric after multi-reporter
aggregation. It never authorizes direct publication of one report.

| Metric | Kind | Roles | Maximum visibility | Traffic-sensitive |
| --- | --- | --- | --- | --- |
| `process_start` | count | all | local | no |
| `process_stop` | count | all | local | no |
| `process_restart` | count | all | operator | no |
| `configuration_rejected` | count | all | operator | no |
| `consensus_health` | health | online reporters | public eligible | no |
| `clock_health` | health | online reporters | operator | no |
| `key_lifecycle_health` | health | online reporters | operator | no |
| `replay_database_health` | health | mix | operator | no |
| `queue_occupancy` | level | data plane | operator | yes |
| `handshake_completed` | count | online reporters | operator | yes |
| `handshake_failure` | count | online reporters | operator | yes |
| `connection_age` | level | online reporters | operator | yes |
| `profile_operations` | count | data plane | operator | yes |
| `padding_data_ratio` | level | data plane | public eligible | yes |
| `kemsphinx_failure` | count | mix, courier | operator | yes |
| `mix_latency` | level | mix | operator | yes |
| `cover_scheduler_health` | health | data plane | public eligible | no |
| `storage_success` | count | courier, store | public eligible | yes |
| `storage_overload` | count | courier, store | public eligible | yes |
| `storage_expiry` | count | store | public eligible | yes |
| `storage_corruption` | count | store | public eligible | no |
| `storage_repair` | count | store | public eligible | no |
| `capacity` | level | all | operator | no |
| `composer_boot` | count | Composer | local | no |
| `composer_lock` | count | Composer | local | no |
| `composer_integrity_failure` | count | Composer | local | no |
| `composer_failure` | count | Composer | local | no |
| `authority_validation_failure` | count | authority | operator | no |
| `observer_submission_rejected` | count | observer | local | no |
| `observer_publication_suppressed` | count | observer | local | no |

Adding a metric, role, value, or visibility class is a protocol and threat-
model change. It requires schema versioning, conformance tests, and
differencing review. A runtime plugin cannot extend the catalog.

## 12. Failure Classification

Owning protocols map internal errors to the catalog before collection. Raw
errors never cross the boundary. A coarse count such as
`configuration_rejected`, `handshake_failure`, `kemsphinx_failure`, or
`authority_validation_failure` does not encode:

- remote endpoint;
- exact parser or signature stage;
- expected or observed bytes;
- profile candidates;
- stack, file, line, or subsystem path;
- retry or connection identity.

Where a protocol needs more than one failure class, it must add a small fixed
enum to this specification. It cannot place an exception message into a label
or diagnostic field.

## 13. Window and Time Rules

The active profile defines one origin and one duration. Roles derive:

```text
window_id = floor((trusted_time - profile_origin) / window_duration)
```

This calculation is illustrative until the exact time profile is selected.
No event time is stored. Clock uncertainty beyond the profile bound changes
`clock_health` and follows the owning protocol's fail-closed time policy.
It does not cause a role to open shorter windows or emit immediate alerts.

Window duration does not adapt to traffic volume. All reporting roles submit
at the same declared schedule with bounded profile-defined scheduling
behavior. Adaptive flush, inactivity flush, and event-triggered window close
are forbidden.

## 14. Suppression and Bucketing

The profile defines an activity unit for every reporting role, one minimum
activity threshold, and common bucket boundaries for compatible metric kinds.
Exact activity is never included in the sealed report.

If activity is below the threshold:

- every traffic-sensitive position is present with `suppressed`;
- non-traffic health and safety positions retain their bucketed value;
- the role still emits its scheduled observer submission;
- the public view emits no small-population inference about that role or
  window.

The suppression threshold is not an anonymity set size. It is one defense
against direct low-volume disclosure. Simulation and trace analysis must show
how it behaves under sparse use, outages, suppression attacks, and colluding
operators.

## 15. IF-11 Aggregate Submission

IF-11 carries one versioned aggregate submission from one authorized online
reporting role to `fog-observer`. It uses the mutually authenticated
role-specific FOG-WIRE context and an aggregate-signing key that is separate
from Noise, node identity, PKI vote, KEMSphinx, receipt, storage, and release
keys.

The final IF-11 definition must bind at least:

- network and schema version;
- observability profile;
- reporting role and authenticated reporter key;
- coarse window and earliest release window;
- exact ordered fixed-shape aggregate body;
- signature suite and purpose-separated signature.

It must define exact size, padding, authentication, retry, replay, expiry,
parser limits, and failure behavior. FOG-WIRE fragmentation does not authorize
variable report geometry or arbitrary metric extensions.

The observer rejects unknown fields, metrics, roles, profiles, values,
duplicate positions, reordered positions, stale windows, early release,
unauthorized signers, invalid signatures, and more than one accepted report
from the same reporter and window. Remote failure remains coarse and
non-amplifying.

## 16. Observer Processing

`fog-observer` maintains three separate stores:

1. a bounded replay/deduplication index for accepted reporter windows;
2. short-lived raw signed submissions required for aggregation and audit;
3. derived operator and public aggregate windows.

The stores use different access rights and retention. Raw submissions expire
after the bounded processing and dispute interval. They are not copied into a
general data lake, backup, search index, ticket, or dashboard cache.

The observer never reads role log files, databases, queues, packet captures,
or container output. It cannot instruct a role to raise verbosity. It has no
credential accepted by data-plane, storage, authority-voting, or release
interfaces.

## 17. Operator View

The protected operator view may show only delayed fixed-window buckets from
metrics whose visibility is `operator` or `public eligible`. It may group by
role and declared public topology class when the active policy permits.

It does not expose:

- reporter or node drill-down for traffic-sensitive metrics;
- arbitrary time ranges or window overlap;
- raw submissions or signature identifiers as chart dimensions;
- correlation across roles, providers, links, or exact failure times;
- downloadable event records;
- queries parameterized by peer, route, connection, packet, or user input.

Access is least privilege. Authentication and authorization failures are
recorded only as coarse local observer counters. Viewing a dashboard cannot
extend the underlying retention period.

## 18. Public View and Differencing Defense

A public aggregate requires all of the following:

- the profile-defined minimum number of distinct authorized reporters;
- the required operator-family and role grouping;
- completion of the release delay;
- non-overlapping source and publication windows;
- bucketed output with no exact totals;
- suppression for low activity, reporter loss, or unsafe composition;
- one fixed set of published dimensions;
- no arbitrary filters, range queries, or node drill-down;
- review of adjacent releases for differencing and intersection leakage.

Public series are not cumulative. If one group or window is suppressed, the
observer must not publish another overlapping total from which it can be
subtracted. A later merge may publish only when the merge rule was fixed in
advance and every released view remains non-subtractable.

Reporter arrival, rejection, absence, and signature metadata are never public
dimensions. Public URLs, cache keys, ETags, and response timing must not vary
by hidden raw reporter state beyond the fixed publication schedule.

## 19. Local Retention and Access

The default implementation keeps sealed local summaries in a bounded volatile
ring. A role profile may instead authorize protected persistence only when it
defines:

- exact maximum window count and age;
- owner and filesystem permissions;
- encryption and key ownership where required;
- atomic replacement and crash behavior;
- deletion and backup exclusion;
- manual export schema;
- consequences of a compromised operator account.

No local report is retained indefinitely. Rotation removes the oldest whole
window. It does not compress old windows into cumulative history.

The Composer stores any permitted summary inside its encrypted vault or
volatile memory. It has no automatic remote diagnostic path. A manually
exported Composer diagnostic contains only a newly created coarse report
selected and previewed by the user, never the raw ring or vault objects.

## 20. Crash and Support Policy

Production profiles disable:

- core dumps and process memory capture;
- automatic panic, exception, or crash upload;
- operating-system crash collection that includes role memory;
- heap, goroutine, thread, profiler, packet, or syscall traces in support
  artifacts;
- automatic attachment of logs, configuration, environment, or database
  files to tickets.

A crash increments only a coarse failure bucket after safe restart when the
owning state machine permits restart. Security-critical state such as replay,
ratchet, consensus, storage, or anchor state still follows its own fail-closed
recovery rules. Observability never authorizes bypass or state recreation.

A manual support artifact uses a separate reviewed schema, shows the exact
fields to the operator or Composer user before export, excludes raw local
summaries by default, and has a fixed size and deletion policy.

## 21. Debug and Test Builds

Production artifacts contain no runtime switch that enables prohibited
logging. Environment variables, signals, command-line flags, configuration
files, remote administration, or observer requests cannot activate packet or
event tracing.

Conformance builds may emit verbose traces only when all of the following
hold:

- the build is distinctly labeled and rejected by production configuration;
- all keys, packets, contacts, routes, and inputs are public synthetic
  fixtures generated for the test;
- the build cannot join a claim-bearing network;
- traces remain outside release artifacts and support bundles;
- tests prove that production builds omit the mode.

## 22. Go Conformance Module

`../observability/` follows a small library shape because future role code
will import the contract, while the observer daemon and wire codec remain
separate responsibilities.

The module provides:

- fixed `Role`, `Metric`, `Level`, `Health`, `Scope`, and value enums;
- role and value-kind validation;
- concurrency-safe saturating in-memory counters;
- an explicit policy with no default constructor;
- one-way collector sealing;
- fixed-shape local and observer reports;
- low-activity suppression;
- delayed observer windows;
- a concurrency-safe bounded volatile local ring;
- deterministic JSON only for conformance and local diagnostics.

It deliberately does not provide:

- arbitrary labels, messages, attributes, exemplars, or strings;
- exact timestamps, durations, sizes, counters, or samples in reports;
- disk persistence or remote log transport;
- OpenTelemetry, Prometheus, syslog, journald, or service-mesh adapters;
- IF-11 decoding, signing, networking, observer aggregation, or public output;
- a numeric production policy.

The absence of those adapters is a security boundary, not an unfinished
convenience layer.

## 23. Conformance and Adversarial Tests

Before an observability profile becomes claim-bearing, tests must prove:

1. every role rejects every metric outside its catalog;
2. every metric rejects the wrong value kind and unknown value;
3. exact counters saturate and never wrap;
4. sealing occurs once and exact counters are inaccessible afterward;
5. local and observer shapes are fixed for role and profile across zero, low,
   threshold, and high activity;
6. low traffic produces `suppressed`, not a missing field or exact count;
7. Composer and FOG-SX roles cannot produce observer submissions;
8. observer reports cannot contain local-only fields;
9. observer release is later than the source window and overflow fails;
10. packet, message, trace, peer, route, capability, address, path, error, and
    timestamp fields are absent from encoded reports;
11. local retention is bounded and chronological after rotation;
12. unknown, duplicate, reordered, stale, oversized, and trailing IF-11 input
    fails before allocation or signature-dependent action;
13. duplicate reporter windows and replay fail without changing public state;
14. missing reporters, low populations, adjacent windows, and overlapping
    queries cannot defeat suppression by differencing;
15. observer loss cannot alter data-plane or consensus behavior;
16. production binaries reject debug/test profiles and do not produce core
    dumps or automatic support uploads;
17. malformed or attacker-controlled values never reach log formatting;
18. raw submissions expire and cannot be recovered from backups or dashboard
    caches after the declared interval.

The current Go module covers items 1 through 11 at the role-local contract
boundary. IF-11, observer, deployment, and public-release tests remain gated
on their exact implementations.

## 24. Operational Alerts

Alerts are delayed state conditions, not event notifications. Candidate alert
classes include:

- consensus `degraded` or `unavailable`;
- clock `degraded` or `unavailable`;
- replay database `unavailable`;
- key lifecycle `degraded` or `unavailable`;
- cover scheduler `degraded` or `unavailable`;
- sustained capacity `high` or `full`;
- sustained storage corruption or repair failure;
- repeated configuration rejection or process restart buckets.

The exact number of windows needed to alert is profile-defined. Alerts never
contain a packet, peer, connection, route, request, error string, or event
time. Immediate local safety shutdown remains the responsibility of the
owning role and does not wait for an alert.

## 25. Deployment Requirements

A claim-bearing deployment must demonstrate:

- container or service-manager logging cannot capture stdout/stderr event
  streams from the role;
- only fixed coarse startup failure output is possible before the collector;
- core dumps and automatic crash collection are disabled effectively, not
  only in source configuration;
- observer credentials cannot access role state or another interface;
- no host port exposes role metrics directly;
- local summary storage and observer raw storage meet declared ownership,
  permissions, capacity, backup, rotation, and deletion rules;
- dashboard and API caches do not outlive source retention;
- production configuration rejects unknown metrics, test profiles, and debug
  modes;
- clock failure, observer outage, low activity, and reporter loss preserve the
  declared fail-closed and suppression behavior.

## 26. Requirements Traceability

| Requirement | Observability response |
| --- | --- |
| `TM-OPS-01` | closed schema, forbidden data classes, coarse windows, suppression, delayed aggregation, bounded retention |
| `TM-NET-01` | no fine timing, event stream, route, peer, or cross-role trace identifiers |
| `TM-NET-03` | cover health, fixed reporting schedule, low-activity suppression, no weakened fallback |
| `TM-ROLE-01` | role-local collectors and keys, no universal log access |
| `TM-ROLE-02` | no packet, replay-tag, route, delay, or next-hop records |
| `TM-ROLE-03` | no request, selection, receipt, storage, or per-source histories |
| `TM-AVAIL-01` | saturating counters, bounded rings, fixed catalogs, bounded submissions |
| `ARC-001` | separate role collectors, observer, state, access, and reporting keys |
| `ARC-007` | one owner for local summaries and aggregate-signing material |
| `ARC-008` | fixed schema, no dynamic labels, bounded windows and retention |
| `ARC-009` | observability never authorizes a privacy-weakening recovery path |
| `IF-11` | delayed authenticated fixed-shape aggregate only |

## 27. Open Activation Decisions

Before enabling IF-11 or public output, FOG must select and validate:

- window origin and duration;
- activity unit and minimum activity per reporting role;
- count, level, latency, ratio, age, queue, and capacity bucket boundaries;
- minimum independent reporters and operator-family diversity per public
  group;
- role-to-observer delay, observer-to-public delay, and merge behavior;
- local, raw observer, derived operator, public, cache, and backup retention;
- the exact IF-11 body encoding, fixed size, padding, signature suite, key
  certification, replay, retry, and expiry rules;
- public dimensions and anti-differencing composition analysis;
- clock-uncertainty behavior and fixed submission scheduling;
- deployment-specific storage, access, deletion, and crash controls;
- simulation and trace evidence for sparse traffic, outages, reporter loss,
  malicious suppression, collusion, and long-term intersection;
- independent privacy and implementation review.

Until those decisions are activated through signed profiles and evidence, the
safe deployment state remains local bounded summaries only, with observer and
public export disabled.

## 28. References

- FOG threat model: `FOG-THREAT-MODEL.md`
- FOG architecture: `FOG-ARCHITECTURE.md`
- FOG adjacent-link protocol: `FOG-WIRE.md`
- FOG KEMSphinx profiles: `FOG-SPHINX-PROFILES.md`
- FOG storage: `FOG-STORAGE.md`
- FOG Composer: `FOG-COMPOSER.md`
- FOG simulation: `FOG-SIMULATION.md`