protected

Only the allowed_importers, and the protected units' own members, may import a modules unit directly.

Example

[contracts.agents-are-api-only]
type = "protected"
modules = ["web/src/agents"]
allowed_importers = ["web/src/app/api"]
support.ts tools.ts app/api/chat/route.ts allowed importer components/chat.tsx lib/run.ts web/src/agents its own members not checked
Only the API route, and the agents themselves, may import an agent directly.

Say web/src/agents holds LLM agents that read API keys and call paid services, and should only run behind API routes:

  • Broken by web/src/components/chat.tsx importing @/agents/support, and by web/src/lib/run.ts importing @/agents/tools.
  • Allowed: web/src/app/api/chat/route.ts importing an agent, and the agents importing each other.
  • Not checked: chat.tsx importing run.ts, which isn't protected, so the chain from chat.tsx to an agent isn't reported, only run.ts's own import. protected only checks direct imports, to keep the list of importers short and deliberate; a forbidden contract can rule out the chains as well.

Restricting a third-party dependency

A language's scheme protects an external package, so that only some of your code may use it:

[contracts.sharp-only-in-images]
type = "protected"
modules = ["npm:sharp"]
allowed_importers = ["web/src/images"]

Say sharp, a native image library, should stay behind one module that resizes images:

  • Broken by web/src/app/profile/avatar.ts importing sharp.
  • Allowed: web/src/images/resize.ts importing sharp, and avatar.ts importing web/src/images/resize.ts.

Other languages' packages work the same, such as gomod:github.com/disintegration/imaging or crate:serde. Since such a selector may match nothing, a misspelt package name isn't an error: the contract is simply kept.

Options

OptionDefault
modulesrequiredSelectors of the protected units.
allowed_importersrequiredSelectors.

Common options

Like every contract type, protected also takes:

OptionDefault
typerequiredThe contract type, such as "layers".
namethe idThe title shown in the output.
severity"error""warn" reports a broken contract without failing the check, for rolling a contract out gradually.
exclude_kinds[analysis] exclude_kindsThe import kinds to leave out, e.g. ["type"].
reexports[analysis] reexports"transparent" sees an import of names from a [pass-through module](../concepts.md#pass-through modules) as imports of the modules that define them, as bundlers load them; "opaque" as an import of the pass-through module. Not for encapsulation.
ignore_imports[]"<selector> -> <selector>" entries: the direct imports between them are left out of the contract.
unmatched_ignore_imports"error"Whether an ignore_imports entry that matches no import breaks the contract ("error"), is a warning ("warn") or is fine ("none").
guidancenoneText shown under each violation, such as how to fix it.