# 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`