config/config.toml is the canonical sample configuration. Validate a selected
base/profile/environment combination before starting a service:
cargo run -p mega2 -- --config config/config.toml config validateConfiguration source diagnostics are available with --show-sources; use
--deny-warnings in automation that must reject ignored or deprecated input.
Secrets are supplied by deployment configuration or supported Vault
SecretRefs and must never be committed.
[monorepo].object_format selects the object-ID algorithm for an empty
Monorepo's initial graph and for live Git service. Canonical values are sha1
(the default), sha256, and blake3. The parser also accepts sha-1 and
sha-256. black3 is not a valid spelling or value. The setting is
restart-required and does not convert an existing repository.
sha1 is the stock Git format. sha256 and blake3 are git-internal / Libra
extensions: normal service advertises object-format={kind} and pack
encode/decode uses *_with_hash_kind. Do not claim interoperability with stock
Git clients for those values (DEFER-B3-01). Cross-kind or wrong-width IDs and
pack trailer mismatches fail closed.
Git Object Format is independent of LFS Digest Algorithm
(Git=blake3/LFS=sha256 and Git=sha256/LFS=blake3 are expressible). LFS
BLAKE3 as a product transfer path is DEFER-B3-LFS-01.
Run mega2 --config <path> service init --yes to create the initial
graph; the command requires an existing config and exits without starting Git
listeners or normal service runtime dependencies. See
protocol.md and plan/plan-20260907.md.
The [cedar] section controls the authorization enforcement switch (ADR-UN-01):
[cedar]
enforcement = "off" # off | shadow | enforceoff(default): do not build or consume authorization data; zero behavior change.shadow: build the store, evaluate, record would-deny logs, but do not change allow decisions.enforce: build the store, evaluate, and deny unauthorized requests.
config validate rejects any other enforcement value, and rejects a
monorepo.admin entry equal to the reserved anonymous principal
User::"__anonymous__" (ADR-UN-06 ⑤). Source diagnostics for cedar.enforcement
are available via config validate --show-sources --format json (the JSON
output includes the field's winning source).
[monorepo].push_policy is "trunk" (default, MonoWriteQueue) or "review"
(CL pipeline; sample in config/config-review.toml). [monorepo].max_push_commits (default 250) bounds first-parent
chain length in trunk morphology only; review morphology keeps the
MAX_CL_CHAIN_COMMITS constant. Both fields are restart-required.
[git].push_auth omitted keeps the existing OAuth / UserStorage chain (review
only). Explicit "token" or "none" requires push_policy = "trunk"; trunk
requires an explicit push_auth. [[git.push_tokens]] entries authorize by
component-boundary path prefix (/foo does not authorize /foobar); omitted
paths means the whole repository. Token values may be literals (IT/tests) or
vault://secret/config/<profile>/git/push_tokens/<name>#<field> SecretRefs.
File-mounted secrets use ${file:...} and are expanded at load.
Trunk HTTP start additionally refuses open change lists, a push_policy
change while push_queue has non-terminal rows (queue_control.last_policy),
and resets blob_paths.indexed_push_id to NULL on a successful empty-queue
morphology switch.
Browser sessions are validated against website Better Auth. The [oauth]
section supplies the website API base URL, accepted session-cookie names, and
the CORS allow-list. The complete trust boundary is documented in
website-auth.md.
Product-email delivery is delegated to the website. Mega2 configuration
contains the website email API base URL and bearer/SecretRef used by the
notification client; see website-mail.md for the
request contract and Compose values.
There is no [mail] section, MailConfig, mail.password SecretRef, or
MEGA_MAIL__* environment override. These removed inputs are rejected rather
than silently ignored.
idgenerator 2.0.0 allows at most 22 bits for worker_id_bit_len + seq_bit_len.
Mega's target layout is 8+8 (256 workers, timestamp shift 16). This process
keeps the existing 6+8 layout (64 workers, shift 14) because ADR-FC-04 forbids
mixing old and new writers, and this deploy cannot fence every previous
worker_id(1) process before the new bits go live.
Worker id is chosen once at startup, in order:
MEGA_ID_GENERATOR_WORKER_IDwhen it parses as an integer in0..=63. Invalid or out-of-range values log a warning (not the raw value) and fall through.- Redis
SET NX PXonmega2:snowflake:worker:<id>with a 30s TTL. The owner refreshes with a compare-and-PEXPIRE token every 15s. Redis errors or a full 0..=63 map fall through. - Stable FNV-1a of
POD_UID, elseHOSTNAME, elsemega2-local, reduced into0..=63.
ID generation after init does not talk to Redis. Logs include source
(Env/Redis/Hash), worker id, bit lengths, and process identity — never
the lease token, Redis URL, or pod secrets.
Notification configuration controls mega2-owned delivery such as in-app,
Slack, and webhook notifications. The email preference requests delivery
through the website API; it does not configure a local mail provider. Refer to
notification.md for behavior and test boundaries.
[storage_events] is a restart-required, default-disabled committed-write
emitter surface (plan-20260912 / WH-01). enabled = true requires
git.storage_only() (push_auth is "token" or "none"); review morphology
is rejected at config validate. installation_id is required when enabled
and is never auto-generated. Target HMAC values are secret_ref URIs; they
are not resolved while disabled.
Target url values must be HTTPS without userinfo, query, or fragment.
Numeric bounds hold even when the table is disabled: max_in_flight is
1..=64, connect_timeout_seconds is 1..=5, request_timeout_seconds is
1..=10, and shutdown_grace_seconds is 0..=10 (WH-14; 0 aborts in-flight
sends at once, and the ceiling is what keeps ADR-WH-03's drain bounded).
Filter entries validate per source dimension. git_paths and lfs_paths
must be absolute canonical paths. oci_repositories must
be canonical lowercase distribution remoteName values, judged by the same
rule as the inbound /v2 router (src/common/oci_name.rs is the single
implementation shared by both callers — WH-14). A name the router would reject
can never match an inbound repository, so such a filter entry is a silently
dead subscription and is rejected rather than accepted. The legacy Capture
filters (agent_tenants / agent_repo_paths) were removed by MTR-57 and are
rejected by the strict unknown-field check. Every list is capped
at 64 entries of 1..=256 bytes each.
Every target secret_ref must use the namespace
vault://secret/config/<profile>/storage_events/targets/<id>/hmac#<field>,
where <id> is the target's own id; config validate enforces the shape
and namespace in both enabled and disabled modes (string-level only — it
never resolves). When enabled, service startup (WH-11) resolves each ref
through the vault and compiles the HMAC key: the resolved value must be
hex:<even-hex> decoding to 32..=256 bytes. Any resolution or encoding
failure fails startup with a redacted error; the resolved value is never
written back into the config snapshot or logged, and the SecretRef URI
stays out of error text. Secrets are seeded/rotated by piping the
hex:<even-hex> value on stdin (--value-stdin is required and is the only
way to supply the value), e.g.:
printf '%s' "$STORAGE_EVENTS_HMAC" | mega2 --config config/config.toml \
config secret set storage_events.targets.ops-main.secret_ref \
--vault-path config/prod/storage_events/targets/ops-main/hmac \
--field value --value-stdinRotation uses config secret rotate with the same arguments and requires a
service restart (no hot reload of [storage_events]).
The HTTPS HMAC transport is wired into AppContext at startup (WH-11): on
enabled configs the resolved targets plus an HttpsEventTransport become the
single application emitter owner on Storage; on disabled configs nothing
is resolved and the emitter stays disabled. Each POST resolves DNS once,
rejects loopback/private/link-local/metadata/mixed results, pins the
connection to that verified address, and keeps the original hostname for TLS
SNI. Four source hooks are installed (WH-03..WH-06): repo.push,
oci.manifest.published, lfs.object.uploaded and lfs.media.finalized.
The two legacy Capture events were retired with MTR-57.
See storage-events.md for the per-source commit points
and the registered coverage gaps.
Target events must be unique literals from the four frozen types, at most 16
targets per table. An empty filter list means that source is not subscribed —
it is not a wildcard.
Upgrading from a release before the WH-14 gates: shutdown_grace_seconds
above 10 and non-canonical oci_repositories entries used to load, and now
fail config validate and service start. Both shipped samples keep
[storage_events] disabled with the targets commented out, so only
deployments that hand-wrote those values are affected; correct them before
upgrading.
[traces] is the enablement and deployment-identity section. enabled defaults to false. server_id is an operator UUID and is required only when enabled; startup does not generate it. scope is an optional operator label. A reload classifies changes to these three fields as restart-required and does not publish them into the live snapshot. git_limits, query_defaults, and protocol budget keys are unknown fields. The sample profiles in config/ keep enabled = false.
.env.test.example documents public test endpoints for PostgreSQL, Redis, and
RustFS. Its MAILPIT_* values are optional website-next SMTP-capture inputs;
mega2 neither reads them nor sends SMTP. The Compose topology and service
profiles are documented in test-infra.md.
traces purge-legacy-capture uses the configured [object_storage] backend for its inventory step. The inventory enumerates only the frozen manifest prefixes; a cloud backend without a provable native version inventory blocks the step, while local records objects without provider versions.
The delete step removes only the frozen inventory of a journal and advances its cursor per object, so an interrupted run resumes; it never drops the legacy tables.