Skip to content

feat: consolidate tracer, fees & CRM into the midaz v4 monorepo#2159

Open
fredcamaral wants to merge 456 commits into
developfrom
feat/monorepo-consolidation
Open

feat: consolidate tracer, fees & CRM into the midaz v4 monorepo#2159
fredcamaral wants to merge 456 commits into
developfrom
feat/monorepo-consolidation

Conversation

@fredcamaral

Copy link
Copy Markdown
Member

What

Consolidates tracer, plugin-fees, and CRM into the midaz v4 monorepo — unified components/ledger binary (onboarding + transaction + CRM + fees on :3002) plus co-located components/tracer (:4020). Single root go.mod (github.com/LerianStudio/midaz/v4), no go.work.

Supersedes the closed #2156 / #2154. The load-bearing difference from those PRs:

⭐ The reporter is NO LONGER part of this consolidation

Earlier attempts folded the reporter in too. That decision was reversed — the reporter is a separately-sellable product coupled to the ledger only over the wire (RabbitMQ + API), not in-process, so it belongs in its own repo. It has been extracted back to LerianStudio/reporter (PR #696) and removed from this monorepo (626 files).

A direct consequence: the private fetcher/pkg/engine dependency is gone from midaz's go.mod — restoring a clean source-available build for external clones (the ledger core never imported it, but a single root module made it a build-time dep for everyone).

Scope

  • Fees embedded in ledger: engine pkg/fee, shared types pkg/feeshared, use cases internal/services/fees; applied at the transaction_create.go fee seam.
  • CRM folded into components/ledger/internal/crm (package tree, no cmd//internal/); routes register under the midaz authz namespace — the tenant-manager policy migration is the X1 release gate (docs/auth/RBAC-NAMESPACES.md, docs/runbooks/v4-x1-rbac-migration-and-rollback.md).
  • Tracer co-located at components/tracer; ledger↔tracer seam over gRPC + mTLS (docs/architecture/ledger-tracer-topology.md).
  • Reporter removed — see LerianStudio/reporter#696.

Verification

https://claude.ai/code/session_01RzpM5cJt1wAqEZQ1mHL63n

…B resolver

T1: buildTracerReserver fails fast when MULTI_TENANT_ENABLED && TRACER_BASE_URL is set but no M2M auth provider is wired (none exists yet) — refuses to ship unauthenticated, tenant-less reserve calls on the transaction hot path. F1: inject the fees Mongo manager into TransactionHandler so the fee seam resolves the tenant fee DB (test in transaction_fee_tenant_test.go).
…ient seams

T4: InjectHTTPContext in do() so all five reservation ops continue the ledger trace instead of starting orphaned roots. T5/T6: remove the orphaned circuit-breaker seam (test-only, never wired). T7: remove dead WithHTTPClient option.
…gauges, replay metric

R1: drop dead CountBackupQueue + lying doc. R2: drop write-only serviceName field/param. R3/R4: parse each backup record once per cycle (track oldest-TTL in the dispatch pass). R5: span attr + counter for the overdraft replay-divergence branch. R6: hoist redis.NewScript to package vars (no per-call SHA1 on the hot path).
…stration tests

K1: CreateHolderWithID treats a document-association collision on the deterministic id as idempotent (re-fetch by id, errors.As+Code), so a doc owned by another holder no longer bricks the run; resolves K4. K2: single-tenant Run injects the onboarding PG conn into ctx before RunTenant. K3: MT loop tests via injectable seams. K5: holderReaderAdapter.Exists table test. K6/K7: bind constant.ExternalAccountType. K8: nil-tenant guard.
N1/N5: span attr key app.request.alias_id -> app.request.instrument_id (8 sites). N2: alias-named locals + span-error/log messages -> instrument. N4: span op-names (incl. Count's misnomer); aliases_<org> collection name kept (renaming orphans prod data). N3: single time.Now() on create. N6: fixed times in tests.
… faults

E5: ErrMidazQueryFailed 500->503, ErrMissingSegmentContext 400->500 (server-misconfig, not a 4xx). E9: ErrTransactionReservationUnavailable(0178) 422->503 (retryable dependency outage). E3/E4: comma-ok settings assertions (no panic). E6: drop dead HTTPError branch (type kept as Swagger anchor). E8: declaration-order contract test + doc.

BREAKING CHANGE: three error codes change HTTP status. Done inside the v4 breaking-change window; documented in docs/plans/2026-06-07-v4-error-status-migration-notes.md and locked by mainline_error_contract_test.go.
…ords persist

Q1: the unmarshal_failure poison record is by definition non-JSON; a JSONB NOT NULL column rejected it (SQLSTATE 22P02), so it could never quarantine and looped in Redis forever. Column is opaque/never queried as JSON -> BYTEA. Migration 000034 is net-new on this branch (edited in place). Adds a testcontainer Insert test with a non-JSON payload.
…e2e comments

S1/S2/S3: assert the HolderID value-copy (a mis-wire to another *string field would have compiled and passed). S4: delete comments referencing an e2e mirror struct that does not exist.
… ctx-aware sleep

B2/B3: retry.go uses commons/backoff.WaitContext(ctx) — interruptible, abandons on cancel (safe redelivery); SleepFunc removed. B1 (third rail): delete BackoffCalculator, route all call sites through commons/backoff.ExponentialWithJitter + min(cap). B4/B5/E7: delete dead deprecated wrappers + orphaned shim re-exports.

Behavior change: full-jitter [0,exp) replaces the old [exp,2*exp] — retry sleeps are smaller on average (canonical AWS full-jitter).
…anager guard

D1/D7: PG/Mongo schema validation agree on empty-field tables (+ parity test); drop dead Mongo matchedTables. D2/D3: fix stale TPL-/Fetcher comments. D4: delete dead sentinel aliases. D5: ValidateSchema matches GetDataSourceSchema not-found wrap. D6: shared validateTenantID predicate. D8: BASE-TABLE introspection drift-lock test. D9: nil-manager panic guard at construction.
Audited 68 Gate-8 findings (54 REAL, 10 PARTIALLY_VALID, 4 FALSE_POSITIVE) -> 38 distinct work units across 4 tiers, with per-finding verdict, chosen fix, and status.
… OpenAPI annotations

Where the response status was semantically wrong, fix the code, not just
the docs. State-machine and uniqueness conflicts are 409 Conflict, not 422
Unprocessable Entity. Done in the v4 breaking window.

BREAKING CHANGE: 7 sentinels now return HTTP 409 instead of 422:
  0087 ErrTransactionIDHasAlreadyParentTransaction
  0088 ErrTransactionIDIsAlreadyARevert
  0093 ErrBalancesCantBeDeleted
  0099 ErrCommitTransactionNotPending
  0199 ErrPackageRange
  0219 ErrBillingRouteOverlap
  0016 ErrBalanceRemainingDeletion

Kept at 422 (payload-content invariants, not state conflicts): 0089, 0090,
0150, 0182.

- pkg/errors.go: UnprocessableOperationError -> EntityConflictError in errorMap
- contract + unit tests: assertion flips to 409
- handlers: @failure annotations (commit/cancel/revert/packages now carry
  both 409 and 422; delete-balance/delete-asset/billing-package -> 409)
- regen: ledger/tracer/reporter specs + postman mirrors
- OpenAPI annotation completeness across ~19 handlers (descriptions, 401/403,
  X-Request-Id, UUID-format params)

Note: EntityConflictError.Error() returns only Message (no code prefix),
unlike UnprocessableOperationError.Error(); the code survives on the typed
.Code field and the wire `code` JSON.
…all components

Phase 5 (schema deepening) + the M13 @name-sweep completion, folded.

Schema deepening:
- tracer pkg/model: Rule/Limit/Scope/AuditEvent/TransactionValidation/
  ValidationResponse/EvaluationResult get swagger:model+@name+@description,
  field examples, enum hints; Rule.Expression carries a CEL example; the
  swaggerignore that hid EvaluationResult's promoted fields is removed.
- fees: 10 billing structs annotated; Calculation.Type enums [percentage,flat];
  FeeCalculate/FeeEstimate.transaction documented (renders as TransactionInput).
- reporter: CreateReportInput.Filters nested-shape description; Pagination.Items
  typed; metricsResponse/errorMetrics/notificationItem/notificationResponse
  exported (MetricsResponse/ErrorMetrics/NotificationItem/NotificationResponse)
  with @name+@description+examples; test mirrors repointed to the exported types.
- CRM: Holder/Instrument deepened to the Account standard; LegalPerson's
  mislabeled @description corrected.

@name sweep (finish M13 for tracer + ledger tail):
- tracer pkg/model enum types (Decision/RuleStatus/LimitStatus/LimitType/
  ActorType/AuditEventType/ResourceType/TransactionType) inline-typed on every
  serialized field; context sub-structs, Actor, and the http/in handler DTOs
  (Create/Update*Input, List*Response, Reserve*, *ActionResponse, ValidationSummary)
  get closing-brace @name directives.
- ledger: pkg/net/http.Pagination -> @name CursorPagination (distinct from the
  page/total Pagination); mmodel LedgerSettings/TracerSettings/AccountingValidation
  named.

Result: zero dotted/package-qualified schema keys across all three specs
(only lib-commons pkg.HTTPError remains). Regenerated ledger/tracer/reporter
api/* + postman/specs/*. Non-breaking: json wire shapes unchanged, only schema
names/descriptions/examples added. ledger security 111/111, parity green.
…(0486)

ErrCommitTransactionNotPending (0099) was returned from three semantically
distinct conditions in the commit/cancel/revert state handlers:
  - status != PENDING (commit)  -> genuine state conflict
  - status != APPROVED (revert) -> genuine state conflict
  - Redis SetNX lock NOT acquired -> concurrency, NOT a status problem

The lock-contention path returned "Invalid Transaction Status / the status
does not allow the requested action", which misleads the client: the status
may be perfectly valid (PENDING) — a concurrent commit just holds the lock.
The honest signal there is "retry shortly", not "fix your request".

New sentinel ErrPendingTransactionLocked (0486), mapped to EntityConflictError
(409, unchanged status), with a retry-worded message. Only the SetNX-not-acquired
site (transaction_state_handlers.go) switches to it; the two genuine
state-conflict sites keep 0099.

BREAKING CHANGE: the transaction lock-contention response now carries error
code 0486 instead of 0099 (HTTP status stays 409). Clients matching on 0099
for that path must handle 0486. Done in the v4 breaking window.
The alias->instrument rename already moved the span attributes to
app.request.instrument_id; this finishes the cleanup:
  - drop the dead "alias_id" entry from constant.UUIDPathParameters (no route
    registers it; the instrument handlers read the "instrument_id" path key)
  - rename leftover aliasID locals/params to instrumentID across the instrument
    handlers, CRM services, and Mongo adapters (they all denote the instrument
    UUID, never an account alias)

No wire impact: span attribute keys were already instrument_id; this only
removes stale config and corrects identifier names.
…O (L8)

The POST /estimates response embedded FeeCalculate — the mutable fee-engine
carrier built on the transaction *input* shape (TransactionInput), carrying the
deprecated `route` field. Introduce an output-only FeeEstimateResult +
FeeAdjustedTransaction projection and serialize that instead. The engine keeps
FeeCalculate as its in-place-mutated carrier; the wire no longer sees it.

BREAKING CHANGE: the `feesApplied` object in POST /v1/organizations/{id}/estimates
now serializes as FeeEstimateResult/FeeAdjustedTransaction instead of FeeCalculate,
and the deprecated `route` field is dropped from the fee-estimate transaction
(use `routeId`). Computed fee values and HTTP status are unchanged.
…ggle

Replace the ledger→tracer reservation call's unauthenticated/M2M-token seam
with a gRPC+mTLS transport, kept behind a TRACER_TRANSPORT=grpc|rest toggle
(default rest for safe rollout). Single-tenant; trusted x-tenant-id
propagation lands in Phase 2.

- proto/reservation/v1: reservation contract mirroring the Go wire types
  field-for-field (amount as decimal-as-string), buf codegen via pinned
  `go run`, make proto/proto-check, check-proto CI job.
- tracer: gRPC ReservationService server delegating to the same use-case
  core the REST handler uses (no logic fork); mTLS on both transports via a
  single shared buildSeamTLSConfig (RequireAndVerifyClientCert).
- ledger: gRPC client behind the TracerReserver port + transport selection;
  client-side mTLS via buildSeamClientTLSConfig; boot-guard reworked from the
  MT-refusal block to an mTLS-material assertion. Removed the dead
  M2MTokenProvider/Bearer seam, preserving the x-tenant-id injection hook.

Cert material loads through lib-commons certificate.Manager for rotation.
Fixes a latent last-WithTransportCredentials-wins bug that would have dialed
plaintext under grpc+mtls. mesh mode dials plaintext for sidecar-terminated
mTLS.
…tion seam

Phase 2 of the ledger↔tracer seam: multi-tenant reservations over the
mTLS/mesh-verified transport. The ledger forwards the request's tenant id to
the tracer on every reservation RPC; the tracer resolves the per-tenant
PostgreSQL pool from that TRUSTED key — the verified mTLS peer is the identity,
so the forwarded x-tenant-id needs no JWT claim behind it.

Ledger (client side):
- REST: populate the injectTenant hook to set X-Tenant-Id from
  tmcore.GetTenantIDContext; omit the header entirely in single-tenant mode.
- gRPC: a single client interceptor appends the tenant to outgoing metadata
  for all five reservation RPCs (no per-method copy-paste). The metadata key is
  derived from the REST header (strings.ToLower(TenantHeader)) so the wire key
  cannot drift across transports.
- The tenant value is never logged on either transport.

Tracer (server side):
- A shared seamtenant.Resolver binds the per-tenant pool via the lib-commons
  tenant manager (tmcore.ContextWithPG/ContextWithTenantID), used by both a
  REST middleware and a gRPC interceptor wired ONLY onto the reservation
  surface. User-facing routes keep their JWT-claim tenant path: the shared JWT
  middleware is skipped on /v1/reservations* (Fiber Group.Use matches by
  prefix, so a separate group would not isolate it) — without the skip the
  mTLS-authenticated seam would be 401'd for lacking a Bearer token.
- Fail-clean invariant: under multi-tenant mode a missing/empty/invalid tenant
  key returns ErrReservationTenantRequired (gRPC InvalidArgument / REST 400) and
  NEVER falls back to a default pool. Single-tenant mode is a no-op.
- MetadataKey derives from HeaderName via strings.ToLower, mirroring the ledger.

Boot-guard unchanged: it discriminates solely on transport security (mtls +
missing material fails fast), never on tenancy.

New sentinel: ErrReservationTenantRequired (0487), mapped as a ValidationError.

Tests: resolver branches (DI pool seam), gRPC interceptor + REST middleware
fail-clean cases, wire-key equality locks on both transports, and an
integration test proving tenant A's reservation is invisible to tenant B over a
real loopback mTLS+gRPC handshake with distinct per-tenant pools.
gRPC is the seam's production transport; REST is retained as a fallback,
selectable with TRACER_TRANSPORT=rest. The empty default now resolves to gRPC
instead of REST.

The tracer's REST surface on :4020 is an operations/configuration surface
(rules, limits, validations — operator-facing, internal), so there is no direct
end-customer access that a uniform mTLS posture would demote; gRPC on its own
seam port is the clean production target.

Operator note: a deploy that wires the tracer (TRACER_BASE_URL set) without
overriding TRACER_TRANSPORT now speaks gRPC, so the tracer must expose its gRPC
seam via TRACER_GRPC_PORT and, under TRACER_TLS_MODE=mtls, both ends need cert
material. Set TRACER_TRANSPORT=rest to keep the previous behavior. gRPC+mTLS
has not yet been soaked end-to-end in a live cluster; the default is set to the
target transport ahead of that soak by deliberate decision.
Add docs/architecture/ledger-tracer-reporter-topology.md — the single canonical
reference for how the ledger, tracer, and reporter products relate at deploy
time. Helm/k8s manifests stay infra-owned and out of this repo by design.

Covers the seven topology contracts, each grounded in code/config (or labeled
as an infra-owned recommendation where no repo artifact expresses it):
product segregation matrix, co-scheduling (separate Deployments + soft
podAffinity, rejecting sidecar/same-pod with the per-tenant PG-pool fan-out
reason), independent scaling (hot-path reserve vs post-commit confirm/release),
graceful absence (TRACER_BASE_URL nil-reserver + failPosture), the mTLS model
(mtls|mesh, cert rotation, trusted x-tenant-id rationale), transport/ports
(gRPC default + REST fallback, TRACER_GRPC_PORT), and the reporter async
RabbitMQ consumer.

Includes a documented gap: neither .env.example yet surfaces the new seam env
vars (they exist only as Go struct tags) — flagged for the Phase 5 docs sweep.
components/crm was a ledger feature living under the components/
product-boundary namespace. Move it to components/ledger/internal/crm
(git mv, history preserved) so the namespace means "separately-buildable
product" again. Pure move: import paths only, zero behavior change.
CI analysis filters roll CRM up under components/ledger; living docs
deferred to the P5 doc sweep.
Phase 5 (final) of the ledger<->tracer seam + CRM consolidation campaign.

Two thrusts across 11 living docs:
1. Stale-path sweep: components/crm -> components/ledger/internal/crm
   across llms.txt, llms-full.txt, STRUCTURE.md, AGENTS.md, CLAUDE.md,
   docs/PROJECT_RULES.md (surgical path strings only), and
   docs/standards/{telemetry,error-handling}.md. Dated docs/plans/* left
   untouched.
2. Surface the reservation-seam env vars (mirroring config.go doc-comment
   semantics): the 7 CLIENT vars in components/ledger/.env.example
   (TRACER_BASE_URL opt-in, TRACER_TIMEOUT_MS, TRACER_TRANSPORT grpc|rest,
   TRACER_TLS_MODE mtls|mesh, TRACER_TLS_{CERT,KEY,CA}_FILE) and the 5
   SERVER vars in components/tracer/.env.example (TRACER_GRPC_PORT distinct
   from :4020, TRACER_TLS_MODE, TRACER_TLS_{CERT,KEY}_FILE,
   TRACER_TLS_CLIENT_CA_FILE verifying the ledger client cert).

README.md + llms-full.txt + llms.txt gain seam-reality prose (gRPC-default
transport toggle, mutual-TLS-or-mesh identity, trusted x-tenant-id
propagation) cross-linked to docs/architecture/ledger-tracer-reporter-topology.md.
Apply standard Go formatting (gofmt and goimports) across the ledger,
tracer, and reporter components to maintain codebase consistency.

Specific changes:
- Sort imports alphabetically in various tests and configuration files.
- Group constant declarations in reservation_server_test.go.
- Remove an obsolete nolint:bodyclose comment in the tracer client.
- Fix struct tag alignment spacing in the reporter model.
- Format multi-line struct definitions in integration tests.

These changes are necessary to adhere to strict Go styling guidelines,
improving overall code readability and easing automated linting checks.
Refactor component Makefiles (infra, ledger, reporter, tracer) to use
shared templates from the mk/ directory to keep build logic DRY. Remove
duplicate or destructive targets (e.g., migrate-down-all, update-deps)
and unneeded component-local makefile_colors.mk files. Add root CI gate
targets (make ci, make ci-tests) to standardize pipeline workflow.

Update shell scripts (check-logs.sh, run-tests.sh) to dynamically
discover Go components via globbing, removing hardcoded references to
deprecated directories (onboarding, transaction). Add executable
permissions to utility scripts. Pin tooling versions (golangci-lint,
gosec, migrate) to ensure deterministic CI builds.
… API

fix(reporter): map env names to valid zap environments to pass validation
Add `@redocly/cli` step in the doc generation scripts to join the
isolated OpenAPI specs from ledger, tracer, and reporter into a single
consolidated definition (`midaz.openapi.json` and `.yaml`). This improves
API discoverability and provides a unified view of the Midaz platform.

Additionally:
- Add a custom `redocly.yaml` ruleset to lint the consolidated spec.
- Add Swagger annotation for the `Items` field in the `Pagination` model
 to clarify its usage and override behavior.
- Update the Postman collection to consistently enable and correctly order
 the `Authorization` header across all operations.
Epic 4.2 — idempotent CreateHolder/CreateInstrument:
- Narrow CRM-local IdempotencyRepo port over the shared transaction Redis
  infrastructure (SetNX/Get/Set); the transaction-typed idempotency
  use-case methods and the money path stay untouched.
- Standard X-Idempotency-Key header; CRM-namespaced keys
  (idempotency:crm:{holder,instrument}:...); an empty key falls back to a
  request-body SHA-256 hash. A replay returns the original entity with
  X-Idempotency-Replayed:true; an in-flight slot returns 409.
- Claim/replay/store orchestrated in the handler layer via a shared
  helper; use-case signatures unchanged. A nil repo disables the feature
  as a passthrough.

Epic 4.3 — instrument-create referential validation:
- CreateInstrument verifies the body-supplied ledgerId/accountId exist
  within the request organization before persisting, via a narrow
  LedgerAccountReader port implemented over the ledger query use case
  (CRM never imports the query package).
- Missing references return 422 UnprocessableOperation (new sentinels
  0488/0489), not the query layer's 404; malformed UUIDs return 400.
contextcheck reports one violation per analysis pass, so the eight
//nolint:contextcheck directives in internal/bootstrap — each suppressing
a documented false positive, since the boot context must not be threaded
into long-lived routes, workers, circuit breakers, and scrape closures —
left seven appearing unused to nolintlint at any given build-graph state,
failing the lint gate non-deterministically.

Exclude contextcheck for the bootstrap package (it has no true positives
to offer in a composition root) and drop the now-redundant directives.
Ygohr and others added 21 commits July 1, 2026 20:21
…d emit events for create/update/delete actions ✨
…tity and emit events for create/update/delete actions ✨
feat(fees): add lib-streaming events to fees flow
Reconcile the 35 incoming develop commits with the monorepo-consolidation
branch (PR #2159). Resolved 31 conflicts + adapted develop features to the
consolidated v4 architecture:

- go.mod: kept v4 module, unioned deps (added redpanda/kadm from develop,
  kept higher tink-go v2.7.0 / franz-go v1.21.3); go mod tidy.
- Removed the dead standalone components/crm/* (folded into ledger) and the
  dead swaggo api artifacts (branch is Huma-generated); kept deletions.
- CRM streaming: adapted develop's alias.* events to the branch's renamed
  instrument.* domain (instrument_{created,updated,deleted,related_party_deleted}),
  kept holder.* events, wired EmitImportant helpers into the consolidated CRM
  services, registered the 7 CRM defs in the catalog, injected the emitter via DI.
- block/unblock transactions: migrated develop's inline-Fiber routes to the
  branch's Huma dual-registration pattern (contract + runtime), regenerated specs.
- v3 -> v4 import fixup across all incoming develop files.
- Fixed a pre-existing branch build break in streaming_integration_test.go
  (fees billing-package event constructors now take the 9-arg signature).

Validation: go build/vet (incl. -tags=integration), lint (0 issues),
unit (14113 tests, 0 failures), integration (all packages green).
Gate 8 review (streaming + code + nil-safety): PASS, 0 issues.
E2E deferred as a tracked follow-up (end-to-end suite is pre-consolidation
topology; separate task once it is updated for CRM-on-:3002).
Add StreamingEnabled and SASL config fields with safe off-by-default values, plus a self-contained BuildStreamingEmitter mirroring the ledger bootstrap. tracerEventDefinitions() is empty for now, and three NoopEmitter fallbacks (disabled, empty brokers, empty catalog) keep the disabled path a no-op with zero behavior change. Documents the streaming block in .env.example. No events are emitted yet.

X-Lerian-Ref: 0x1
Build the lib-streaming emitter once in bootstrap and hold it on Service, and register a conditional SIGTERM producer-drain runnable (registered only when streaming is enabled and a close hook exists; close-once and nil-safe). Also make the CloudEvents source config-driven via STREAMING_CLOUDEVENTS_SOURCE, defaulting to lerian.midaz.tracer. No events are emitted yet; the disabled path stays a NoopEmitter with zero behavior change.

X-Lerian-Ref: 0x1
Add a nil-safe Streaming (libStreaming.Emitter) field to all 12 rule and limit command structs and set it post-construction from the shared bootstrap emitter via initRuleService and initLimitService. Constructors are unchanged, so the field defaults to nil and existing tests are unaffected. No events are emitted yet; this only establishes the injection seam for a later emission epic.

X-Lerian-Ref: 0x1
Add the six Rule event wire contracts (rule.created/updated/activated/deactivated/drafted/deleted) plus a shared RuleScopePayload, each following the 4-part events pattern (Definition, Payload typed independently of the domain, constructor, ToEmitRequest). The payload fence keeps name, description, expression, and the compiled program off the wire, locked by JSONShape tests that populate those fields on purpose. Events are not registered in the catalog or emitted yet.

X-Lerian-Ref: 0x1
Populate tracerEventDefinitions() with the six Rule lifecycle event definitions (the single registration point feeding both the catalog and routes) and add a bijection test locking definitions to routes with no ghost topics. When streaming is enabled without brokers, degrade to a NoopEmitter (Warn) instead of failing startup; other load-config errors still propagate. Replace the obsolete empty-catalog test locks with coverage assertions.

X-Lerian-Ref: 0x1
Emit the six rule.* lib-streaming events (created, updated, activated, deactivated, drafted, deleted) after the transaction commits, via private emit<Event>Event helpers routed through EmitImportant (IMPORTANT posture: nil-safe, never fails the request; idempotent no-op transitions emit nothing). Add a clock to DeleteRuleService so rule.deleted sources its timestamp deterministically.

X-Lerian-Ref: 0x1
Add the six Limit event wire contracts (limit.created/updated/activated/deactivated/drafted/deleted) following the 4-part events pattern, reusing the shared scope payload and adding a TimeOfDay formatter. The fence keeps name, description, and the monetary maxAmount off the wire (JSONShape tests assert their absence). Register all six in tracerEventDefinitions() and extend the catalog/routes bijection test to lock all twelve rule and limit events.

X-Lerian-Ref: 0x1
Emit the six limit.* lib-streaming events (created, updated, activated, deactivated, drafted, deleted) after the transaction commits, via private emit<Event>Event helpers routed through EmitImportant (IMPORTANT posture: nil-safe, never fails the request; idempotent and no-change paths emit nothing). The delete path reuses the command's existing clock for the deleted timestamp. Completes tracer emission for all twelve rule and limit events.

X-Lerian-Ref: 0x1
Add a one-shot midaz-redpanda-init service to the infra compose that pre-provisions the full platform topic set (ledger/CRM/fees from midazEventDefinitions() + the 12 tracer rule/limit topics from tracerEventDefinitions()) idempotently. Add a build-tagged (integration) TestStreamingSmoke that emits all 12 tracer lifecycle events through the real emitter, consumes them back from Redpanda, and asserts the CloudEvents headers (ce-type/ce-subject/ce-tenantid) plus the payload fence. The consume budget counts only this run's tenant so the smoke is re-runnable against a reused broker. Promote franz-go/pkg/kadm to a direct dependency.

X-Lerian-Ref: 0x1
Group the internal events import separately, guard BuildStreamingEmitter with an early ctx.Err() check before broker-bound work, trim call-graph narration from the limit delete doc comments, and drop the CRM architecture comparison from the event catalog doc.

X-Lerian-Ref: 0x1
Absorve os 3 chore(deps) de origin/develop (2026-07-03):
- lib-auth v2.8.1->v2.9.0 (security hotfix: isolamento authz cross-product)
- lib-streaming v1.6.2->v1.7.0 (aditivo; franz-go v1.21.3)
- clear .lerianstudiolibignore pins

Resolucao: go.mod/go.sum via nosso superset + tidy (mantem tink-go v2.7.0
do CRM KMS, kmsg sobre kadm); pkg/streaming/tenant.go mantem o nosso (superset
com guard ctx==nil). build ./... + go test ./pkg/streaming/... verdes.

Claude-Session: https://claude.ai/code/session_018JBQfQo2BrEhswe3npMqv8
Resolves the two GoFiber MEDIUM findings blocking the required
'validate / Security' check on PR #2159 (both pre-existing, not
introduced by the develop re-sync):

- CVE-2026-44332 (username enumeration via BasicAuth timing oracle):
  bump gofiber/fiber/v3 v3.2.0 -> v3.3.0 (indirect, via humafiber).
  Real upstream patch.
- CVE-2026-45045 (X-Real-IP spoofing via Header.Add in proxy
  BalancerForward): affects gofiber/fiber/v2 v2.52.13, the HTTP runtime.
  No upstream fix exists (v2.52.13 is the latest v2; fix only in v3).
  midaz does not use fiber's proxy/BalancerForward middleware, so the
  vulnerable path is unreachable. Suppressed in .trivyignore with a
  documented, dated (exp:2027-01-05) justification.

Verified: go build ./... green; trivy fs confirms 44332 gone and
45045 suppressed (0 findings) with the ignore file, still flagged
without it.

Claude-Session: https://claude.ai/code/session_018JBQfQo2BrEhswe3npMqv8
ClaraTersi and others added 5 commits July 6, 2026 10:31
feat(tracer): emit rule and limit lifecycle events via lib-streaming
Direct pushes to develop bypass the PR huma golden-file gate. This
push-triggered workflow regenerates the ledger and tracer Huma OAS 3.1
dumps from code, verifies the cross-plane spec locks, and pushes a
[skip ci] fix-up commit when a direct push left the committed specs
stale. Auth: MIDAZ push-bot App token for the protected-branch push;
private modules via MANAGE_TOKEN; commit GPG-signed as the CI CD user.

Claude-Session: https://claude.ai/code/session_013kJ9BuhVsVJXVBivdKLNt2
The tier-2 direct-push fix-up regenerated only the two components/*/api
Huma dumps, but the required check-docs gate diffs those dumps AND their
consolidated postman/specs hub (drift_check: git diff --exit-code --
'components/*/api' postman/specs). A direct push changing a ledger/tracer
route drifts both, yet the workflow committed only the dumps — leaving
postman/specs stale, so it did NOT reproduce what the gate catches.

Reuse `make generate-docs` (the same generate-docs.sh the gate runs under
CHECK_DOCS_REGEN=1) in place of two hand-rolled `go test -update` steps, so
the workflow regenerates the full gate-enforced set and divergence is
impossible by construction. Expand the fix-up pathspec to the gate's
verbatim 'components/*/api' postman/specs. Toolchain stays Go + Node/npm
(no swag, no Docker); header comment corrected to match.

Claude-Session: https://claude.ai/code/session_013kJ9BuhVsVJXVBivdKLNt2
…aths

Wave-H security-review Mediums, supervisor fixes:
- The App token was minted as the first step with the App's full
  installation permission set, live on the runner while the regen steps
  execute third-party Go/Node dependency code. Now minted immediately
  before the push step and scoped with permission-contents: write.
- on.push.paths omitted pkg/**, so a dump-affecting change flowing only
  through shared pkg/ code (RFC 9457 Error schema) never triggered the
  fix-up — the same reason release.yml declares pkg/ as a shared_path.

Claude-Session: https://claude.ai/code/session_013kJ9BuhVsVJXVBivdKLNt2
Second re-sync of the consolidation branch with develop (76 commits since
merge-base 1029ff0). develop's improvements adopted on top of the consolidated
structure; the branch's spirit (folded CRM/fees/tracer, v4 module, unified
ledger binary) preserved.

Resolution decisions:
- Service Discovery (new develop subsystem): adopted. libsd + pkg/servicediscovery
  wired into the ledger bootstrap; our tracer teardown grafted into develop's
  refactored launcherApps() helper. CRM-standalone SD wiring dropped (CRM is
  folded, sharing the ledger process identity).
- balance.changed streaming, external-account create, asset-delete external
  cascade: taken from develop.
- CRM PII-safe span attributes: adopted develop's repositoryInputAttributes()
  helper (DRY, tested) over our inline booleans; instrument index_tracker package
  fixed from the alias-path merge artifact.
- Standalone components/crm service: kept DELETED; develop's index-once fix
  landed via the folded internal/crm path.
- v3->v4 module sweep across all .go (develop is still on v3).
- go.mod: lib-commons v5.9.0, lib-streaming v1.8.0, lib-service-discovery v0.6.0,
  toolchain go1.26.5; golangci gate kept at our v2.12.2 + Go 1.26.5.
- Structured logging (T6) kept over develop's fmt.Sprintf-in-logger; overdraft
  "not enabled" log kept at Debug (T7, per-transaction path) with develop's
  extracted env-name constant.
- composition Type now ACCEPTS external, mirroring develop's CreateAccountInput
  (enables creating distinct external accounts for the br-spb rail). Dropped the
  invalidstrings=external block and the now-orphaned validateInvalidStrings
  validator (validator + registration + translation), aligning with develop.

Gates: go build ./... clean; make lint 0 issues; make test-unit 14366 pass / 6 skip / 0 fail.

Claude-Session: https://claude.ai/code/session_01Y5b5NhVjTez11DHGCEfJVk
Unblocks PR #2159 CI:

- Tracer Dockerfile builder was golang:1.26.4-alpine, so the shipped
  binary stamped stdlib 1.26.4 and Trivy flagged CVE-2026-39822 (HIGH,
  os.Root symlink-following directory traversal), fixed in Go 1.26.5.
  Ledger already builds on 1.26.5; align tracer to match.
- lib-commons/v5 v5.9.0 -> v5.10.0 to satisfy the Lerian Library
  Version Check gate. Build (all packages) and unit tests green.

Claude-Session: https://claude.ai/code/session_01TD7yfr219zoX4LQAVtbAPd
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: build Makefile, Dockerfile and local-stack definitions area: ci/cd GitHub Actions workflows and release configuration area: crm CRM component area: dependencies Go module dependencies area: documentation Documentation and markdown content area: infra Infrastructure component area: ledger Ledger component area: migrations SQL migrations area: pkg Reusable public packages area: scripts Build and tooling scripts area: tests Unit, integration and end-to-end tests size/XL PR changes >= 1000 lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants