Ctrl K
ComunidadGuías…

arch-go.yml

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
version: 1

# Architecture rules for the modular-monolith bounded contexts.
# See docs/adr/0004-modular-monolith-bounded-contexts.md for the rationale.
#
# Strict mode: bootstrap is for production wiring only. Per-context
# internal code may NOT import its own context's bootstrap; tests reach
# for the per-context testkit instead. Cross-context tests import only
# **.<Y>.api or **.<Y>.testkit, never **.<Y>.bootstrap and never
# **.<Y>.internal.**. testkit packages are pinned to their own context
# so cross-context test allowances cannot be exploited as transitive
# paths to other bounded contexts.

dependenciesRules:
  # identity context internals may import only identity's own api +
  # internals + test-fixture surface, plus platform packages. Other
  # contexts that need identity functionality go through identity/api.
  - package: "**.identity.internal.**"
    shouldOnlyDependsOn:
      internal:
        - "**.identity.api"
        - "**.identity.api.**"
        - "**.identity.internal.**"
        - "**.identity.testkit"
        - "**.attrkeys"
        - "**.httpserver"
        - "**.secretseal"  # shared AES-256-GCM sealer: ssoconfig seals the OIDC client secret with it (issue #496 extracted it)
        - "**.hmactoken"  # shared v1 HMAC token envelope: satoken builds its service-account access token on it (ADR-0013 generalizes the host-token machinery; shared via internal/ so identity does not import endpoint)
        - "**.revocationset"  # shared per-replica revocation snapshot: service-account tokens check it as host tokens do (issue #1226)
        - "**.testdb"

  # endpoint context internals.
  - package: "**.endpoint.internal.**"
    shouldOnlyDependsOn:
      internal:
        - "**.endpoint.api"
        - "**.endpoint.api.**"
        - "**.endpoint.internal.**"
        - "**.endpoint.testkit"
        - "**.attrkeys"
        - "**.httpserver"
        - "**.hmactoken"  # shared v1 HMAC token envelope: signedtoken builds its host bearer token on it (extracted so satoken can reuse the same primitive without a cross-context import)
        - "**.revocationset"  # shared per-replica revocation snapshot, which service-account tokens also use (issue #1226)
        - "**.identity.api"  # identity.api.AuditEvent, encoded into the enrollment.revoke audit entry
        # auditoutbox is the shared transactional audit outbox: a revoke commits its audit entry in its own transaction (issue #1225).
        - "**.auditoutbox"
        - "**.testdb"

  # rulecontent context internals (ADR-0021). The eighth context, carved out of rules at issue #766 because rule content acquired
  # its own storage and stopped being a projection of the catalog.
  #
  # Deliberately the NARROWEST allow-list of any context: rulecontent produces rule definitions and evaluates nothing, so it needs
  # no other context's api at all. If an entry ever has to be added here, that is worth reading as a signal that evaluation is
  # leaking back into content rather than as a missing permission.
  - package: "**.rulecontent.internal.**"
    shouldOnlyDependsOn:
      internal:
        - "**.rulecontent.api"
        - "**.rulecontent.api.**"
        - "**.rulecontent.internal.**"
        - "**.sqlhelpers"
        - "**.testdb"

  # rules context internals. Cross-context test allowance is
  # **.detection.testkit (and only testkit); a transitional
  # **.detection.bootstrap + **.detection.testharness pair existed in
  # earlier revisions and is no longer permitted.
  - package: "**.rules.internal.**"
    shouldOnlyDependsOn:
      internal:
        # versionedset is the shared versioned single-row JSON set (issue #1109): one row holding a whole set, replaced under a
        # row lock with a conditional version check and a strictly increasing database-clock stamp. The watched-path set and the
        # reachable-address set are the same shape in two contexts, and the ordering guarantees are too subtle to keep in two
        # copies that can drift.
        - "**.versionedset"
        # catchup is the shared catch-up policy (issue #1071): one decision about when a host is owed a state again.
        - "**.catchup"
        # installerscript is the one definition of a package installer script that the agent (which attaches the package's
        # signature) and suspicious_exec (which trusts it) must share exactly, or a signature could be attributed to one path
        # while the alert names another (issue #1161).
        - "**.installerscript"
        # auditoutbox is shared server platform code (issue #1070 moved it out of this context so response could use it too);
        # detection-config changes and watched-path replacements commit their audit entries through it.
        - "**.auditoutbox"
        - "**.rules.api"
        - "**.rules.api.**"
        - "**.rules.internal.**"
        - "**.rules.testkit"
        - "**.attrkeys"
        - "**.httpserver"
        # sqlhelpers is shared DB platform code, the same category as httpserver above, and is already allow-listed for the
        # detection, response and visibility internals. Rules is simply the context that had not needed it until the
        # monitor-match counters wanted the common deadlock retry (#813) rather than a fourth copy of it.
        - "**.sqlhelpers"
        - "**.endpoint.api"
        - "**.identity.api"  # identity.api.AuditRecorder for policy.{create,update,delete} audit
        - "**.detection.api"
        - "**.detection.api.**"
        - "**.detection.testkit"
        # rulecontent.api is the supplier seam ADR-0021 defines: rulecontent produces rule definitions, rules consumes and
        # evaluates them, exactly as rules already consumes detection.api's GraphReader.
        - "**.rulecontent.api"
        - "**.rulecontent.api.**"
        - "**.testdb"

  # response context internals.
  - package: "**.response.internal.**"
    shouldOnlyDependsOn:
      internal:
        # versionedset is the shared versioned single-row JSON set (issue #1109): one row holding a whole set, replaced under a
        # row lock with a conditional version check and a strictly increasing database-clock stamp. The watched-path set and the
        # reachable-address set are the same shape in two contexts, and the ordering guarantees are too subtle to keep in two
        # copies that can drift.
        - "**.versionedset"
        # catchup is the shared catch-up policy (issue #1071): one decision about when a host is owed a state again.
        - "**.catchup"
        # auditoutbox is the shared transactional audit outbox (issue #1070): a containment change commits its audit entry in the
        # transaction that records the change and queues its command, rather than recording the row afterwards.
        - "**.auditoutbox"
        - "**.response.api"
        - "**.response.api.**"
        - "**.response.internal.**"
        - "**.response.testkit"
        - "**.attrkeys"
        - "**.httpserver"
        - "**.endpoint.api"
        - "**.identity.api"
        - "**.sqlhelpers"
        - "**.testdb"
        # control is the shared gRPC wire contract for the agent control channel (a platform package like httpserver/sqlhelpers,
        # generated from internal/control/control.proto). The response gateway implements its server side.
        - "**.control"
        # netaddr parses the address notation an operator writes wherever configuration names hosts or ranges (issue #1059). A
        # platform package like httpserver: the trusted-proxy list and the reachable-address set are written by the same operator in
        # the same notation, and a second parser would eventually disagree with this one about a bare IPv6 literal.
        - "**.netaddr"

  # detection context internals.
  - package: "**.detection.internal.**"
    shouldOnlyDependsOn:
      internal:
        - "**.detection.api"
        - "**.detection.api.**"
        - "**.detection.internal.**"
        - "**.detection.testkit"
        - "**.attrkeys"
        - "**.httpserver"
        - "**.endpoint.api"
        - "**.identity.api"
        - "**.rules.api"
        - "**.visibility.api"  # ADR-0015 cutover: intake fans out to EventLog/EventArchive, the processor claims from EventLog, and correlation/evidence read the EventArchive
        - "**.sqlhelpers"
        - "**.coordination.leader"  # MySQL-advisory-lock leader coordinator: pipeline.Runner gates retention + process-TTL on it
        - "**.secretseal"  # shared AES-256-GCM sealer: the store seals webhook signing secrets, the delivery worker opens them (issue #496)
        - "**.testdb"

  # observability context internals (issue #374). A sixth bounded context (an amendment to ADR-0004's
  # original five) that owns the runtime trace-sampler settings: its store + admin handler. It imports
  # the shared sampler infra (internal/observability/tracing) for the Settings domain type, and
  # identity.api for the authz chokepoint + audit recorder its admin endpoint funnels through.
  - package: "**.server.observability.internal.**"
    shouldOnlyDependsOn:
      internal:
        - "**.server.observability.internal.**"
        - "**.observability.tracing"
        - "**.identity.api"
        - "**.attrkeys"
        - "**.httpserver"
        - "**.testdb"

  # visibility context internals (ADR-0015): ingestion + the event store. The
  # MySQL event_queue (EventLog) store depends only on visibility.api; the
  # ClickHouse EventArchive lands later and will add httpserver (TimeRange).
  - package: "**.visibility.internal.**"
    shouldOnlyDependsOn:
      internal:
        - "**.visibility.api"
        - "**.visibility.api.**"
        - "**.visibility.internal.**"
        - "**.visibility.testkit"
        - "**.httpserver"          # TimeRange on the EventArchive correlation read
        - "**.sqlhelpers"
        - "**.testdb"
        # The shared otelsql span policy, so the ClickHouse archive traces queries exactly as the MySQL pool does.
        - "**.observability.tracing"

  # ----- testkit packages -----------------------------------------------------
  #
  # testkit is a curated test-fixture surface for each bounded context.
  # Pinning its dependencies to its own context (plus platform) prevents
  # cross-context tests from using testkit as a transitive sneak-in
  # path to a third context: even though rules.internal is allowed to
  # import detection.testkit, detection.testkit cannot pull in
  # identity/endpoint/response. arch-go inspects direct imports only,
  # so this defence-in-depth rule is what makes the layered allow-list
  # actually airtight.

  - package: "**.identity.testkit"
    shouldOnlyDependsOn:
      internal:
        - "**.identity.api"
        - "**.identity.api.**"
        - "**.identity.bootstrap"     # testkit.ApplySchema wraps bootstrap.ApplySchema
        - "**.identity.internal.**"
        - "**.attrkeys"
        - "**.httpserver"
        - "**.sqlhelpers"
        - "**.testdb"

  - package: "**.endpoint.testkit"
    shouldOnlyDependsOn:
      internal:
        - "**.endpoint.api"
        - "**.endpoint.api.**"
        - "**.endpoint.bootstrap"
        - "**.endpoint.internal.**"
        - "**.attrkeys"
        - "**.httpserver"
        - "**.sqlhelpers"
        - "**.testdb"

  - package: "**.rules.testkit"
    shouldOnlyDependsOn:
      internal:
        - "**.rules.api"
        - "**.rules.api.**"
        - "**.rules.bootstrap"
        - "**.rules.internal.**"
        - "**.attrkeys"
        - "**.httpserver"
        - "**.sqlhelpers"
        - "**.testdb"

  - package: "**.response.testkit"
    shouldOnlyDependsOn:
      internal:
        - "**.response.api"
        - "**.response.api.**"
        - "**.response.bootstrap"
        - "**.response.internal.**"
        - "**.attrkeys"
        - "**.httpserver"
        - "**.sqlhelpers"
        - "**.testdb"

  - package: "**.detection.testkit"
    shouldOnlyDependsOn:
      internal:
        - "**.detection.api"
        - "**.detection.api.**"
        - "**.detection.bootstrap"
        - "**.detection.internal.**"
        - "**.rules.api"              # Replay runs rulesapi.Rule against fixtures
        - "**.visibility.api"         # ADR-0015: the detection store now requires an EventArchive (correlation + evidence reads)
        - "**.visibility.testkit"     # MemArchive: the in-memory EventArchive the fixtures seed instead of the dropped MySQL events table
        - "**.attrkeys"
        - "**.httpserver"
        - "**.sqlhelpers"
        - "**.testdb"

  - package: "**.server.observability.testkit"
    shouldOnlyDependsOn:
      internal:
        - "**.server.observability.bootstrap"
        - "**.server.observability.internal.**"
        - "**.observability.tracing"
        - "**.attrkeys"
        - "**.httpserver"
        - "**.testdb"

  - package: "**.visibility.testkit"
    shouldOnlyDependsOn:
      internal:
        - "**.visibility.api"
        - "**.visibility.api.**"
        - "**.visibility.bootstrap"
        - "**.visibility.internal.**"
        - "**.httpserver"          # MemArchive mirrors the EventArchive correlation read (TimeRange)
        - "**.testdb"

  # ----- api/ packages: pure-types, no cross-context internals ----------------
  #
  # Each <X>/api/ contains domain value types + interface declarations; any
  # executable cross-context wiring belongs in bootstrap/ or internal/. The
  # rule catches accidents like "rules.api gained a helper that imports
  # detection/internal/mysql for convenience" before they ship.
  #
  # rules/api carries an explicit cross-context exception: it re-exports
  # detection/api types (Event, Process, Finding, NullRawJSON, GraphReader,
  # TimeRange) as type aliases so the catalog rule files can implement
  # rulesapi.Rule.Evaluate without importing detection/api directly. The
  # alias is a deliberate design choice; this rule names it.

  - package: "**.identity.api"
    shouldOnlyDependsOn:
      internal:
        - "**.identity.api.**"
        - "**.attrkeys"
        - "**.httpserver"
        - "**.sqlhelpers"

  - package: "**.endpoint.api"
    shouldOnlyDependsOn:
      internal:
        - "**.endpoint.api.**"
        - "**.attrkeys"
        - "**.httpserver"
        - "**.sqlhelpers"

  - package: "**.rules.api"
    shouldOnlyDependsOn:
      internal:
        # catchup is the shared catch-up policy (issue #1071): the watched-path command carries its status already mapped
        # onto that vocabulary, by the context that owns the lifecycle, rather than cast from its spelling here.
        - "**.catchup"
        - "**.rules.api.**"
        - "**.detection.api"          # alias re-export of detection/api types
        - "**.attrkeys"
        - "**.httpserver"
        - "**.sqlhelpers"

  - package: "**.response.api"
    shouldOnlyDependsOn:
      internal:
        # catchup is the shared catch-up policy (issue #1071): Status.Catchup maps this context's lifecycle onto it, here
        # rather than at each consumer, because this is the file a rename happens in.
        - "**.catchup"
        - "**.response.api.**"
        - "**.attrkeys"
        - "**.httpserver"
        - "**.sqlhelpers"

  - package: "**.detection.api"
    shouldOnlyDependsOn:
      internal:
        - "**.detection.api.**"
        - "**.visibility.api"          # Event envelope alias: the canonical type lives in visibility (ADR-0015)
        - "**.attrkeys"
        - "**.httpserver"
        - "**.sqlhelpers"

  - package: "**.visibility.api"
    shouldOnlyDependsOn:
      internal:
        - "**.visibility.api.**"
        - "**.httpserver"          # TimeRange on the EventArchive read; api is pure types/interfaces (no attrkeys/sqlhelpers)

  # ----- platform packages: allow-list of acceptable internal deps ------------
  #
  # Plan §"Platform packages": "Platform packages MUST NOT import any context."
  # An earlier revision encoded this as a deny-list (shouldNotDependsOn each
  # bounded context); Gemini's review on PR #100 pointed out that the
  # deny-list is not future-proof: adding a sixth bounded context would
  # require updating every platform rule by hand. The allow-list form
  # (shouldOnlyDependsOn the explicit set of allowed internal-graph deps)
  # auto-excludes any new context: a future server/<new-context>/ package
  # is implicitly forbidden because it isn't named in the allow.
  #
  # The allow-list enumerates: every other platform package, the repo-level
  # internal/ tree (envparse + observability today), and stdlib/third-party
  # imports (which arch-go's "internal:" key doesn't restrict; third-party
  # imports flow through automatically, no listing needed).
  #
  # Patterns use full module paths because `**.bootstrap` would otherwise
  # also match each context's bootstrap, and `**.testdb` would also match
  # testdb/full (the cross-context fixture composer that legitimately
  # imports each testkit).

  - package: "github.com/rawtrail/rawtrail/server/config"
    shouldOnlyDependsOn:
      internal: &platform_allow
        - "github.com/rawtrail/rawtrail/server/config"
        - "github.com/rawtrail/rawtrail/server/bootstrap"
        - "github.com/rawtrail/rawtrail/server/httpserver"
        - "github.com/rawtrail/rawtrail/server/metrics"
        - "github.com/rawtrail/rawtrail/server/attrkeys"
        - "github.com/rawtrail/rawtrail/server/apidocs"
        - "github.com/rawtrail/rawtrail/server/sqlhelpers"
        - "github.com/rawtrail/rawtrail/server/ui"
        - "**.internal.**"

  - package: "github.com/rawtrail/rawtrail/server/bootstrap"
    shouldOnlyDependsOn:
      internal: *platform_allow

  - package: "github.com/rawtrail/rawtrail/server/httpserver"
    shouldOnlyDependsOn:
      internal: *platform_allow

  - package: "github.com/rawtrail/rawtrail/server/metrics"
    shouldOnlyDependsOn:
      internal: *platform_allow

  - package: "github.com/rawtrail/rawtrail/server/attrkeys"
    shouldOnlyDependsOn:
      internal: *platform_allow

  - package: "github.com/rawtrail/rawtrail/server/apidocs"
    shouldOnlyDependsOn:
      internal: *platform_allow

  - package: "github.com/rawtrail/rawtrail/server/sqlhelpers"
    shouldOnlyDependsOn:
      internal: *platform_allow

  # auditoutbox sits with the platform packages but carries ONE context dependency, deliberately: the audit event it encodes and the
  # recorder a drain delivers to are identity's contract (ADR-0021), so no encoding of an audit row can be written without naming
  # them. It is listed apart from *platform_allow rather than folded into it so that exception stays visible: a second context
  # dependency here would mean the package had stopped being platform code.
  # catchup is the shared catch-up policy: when a host should be sent a state its latest command did not deliver, and the sweep that
  # asks (issue #1071). Pure platform code with no context dependency at all, since a context maps its own command onto catchup.Latest
  # rather than the package naming any context's types.
  - package: "github.com/rawtrail/rawtrail/server/catchup"
    shouldOnlyDependsOn:
      internal: *platform_allow

  - package: "github.com/rawtrail/rawtrail/server/auditoutbox"
    shouldOnlyDependsOn:
      internal:
        - "github.com/rawtrail/rawtrail/server/identity/api"
        - "github.com/rawtrail/rawtrail/server/sqlhelpers"
        - "github.com/rawtrail/rawtrail/server/testdb"

  # NOTE: server/testdb itself is a Go-leaf and imports no context, but its
  # sub-package server/testdb/full is the cross-context fixture composer and
  # imports each context's testkit. arch-go's package patterns are prefix-
  # matched (github.com/rawtrail/rawtrail/server/testdb matches both leaf + full),
  # so a single rule can't distinguish them. The leaf invariant is verified
  # by inspection (server/testdb/open.go imports nothing in server/<X>/) and
  # by the per-testkit rules below preventing transitive sneak-ins.

  - package: "github.com/rawtrail/rawtrail/server/ui"
    shouldOnlyDependsOn:
      internal: *platform_allow

  # ----- bootstrap/ access: enforced indirectly --------------------------------
  #
  # Plan §"Architecture lint" prescribes "Allow only `cmd/**` and
  # `test/integration/**` to import any `bootstrap/` package."
  # arch-go v1.7.0 exposes only "depends-on" rule families
  # (shouldOnlyDependsOn / shouldNotDependsOn), not the inverse
  # "shouldOnlyBeAccessedBy" the plan sketched against v2. The
  # invariant is enforced indirectly by the importer-perspective
  # rules already in this file:
  #
  #   - Platform packages cannot import any context (incl. bootstraps).
  #   - <X>/api packages cannot import any other context's bootstrap
  #     (the api-purity allow-list excludes it).
  #   - <X>/internal/* packages cannot import another context's
  #     bootstrap (each context's allow-list lists only that context's
  #     own packages + cross-context **.<Y>.api / **.<Y>.testkit).
  #   - <X>/testkit may import its OWN context's bootstrap (per-testkit
  #     rule); other contexts' testkits are not in any internal
  #     allow-list, so cross-context testkit imports go through testkit
  #     too, not bootstrap.
  #
  # The remaining production-reachable bootstrap importers are
  # cmd/rawtrail-{server,ingest} and each <X>/testkit. New packages
  # outside those slots would have to be added explicitly; code review
  # is the gate. If arch-go ever ships a "shouldOnlyBeAccessedBy" rule
  # family in v2, this section becomes the single declarative
  # source of truth instead of a property derived from many other
  # rules.