Skip to content

Configuration

Every non-trivial Matrix package needs configuration: log levels, feature flags, default models, cache sizes, timeouts, references to credentials. The SDK provides a layered resolver — resolveConfiguredPackageConfig — that merges defaults with environment, file, and service overlays. This section is the field guide.

Pages in this section

PageWhat you'll learn
Config schemaThe JSON Schema declaration in matrix.json and how it drives validation/explain tooling.
DefaultsThe config.defaults JSON file — what to put in it and what NOT to.
OverridesThe four overlay layers: env → file → service → caller-overrides.
Secrets referencesWhy credentials never go in config.defaults, and the Factotum boundary.
Config explainmatrix config explain <package> — debugging which value came from which layer.

The mental model

defaults (in package)
   ↓ deep-merge
profile (caller-supplied)
   ↓ deep-merge
provider 1 (e.g., env vars)
   ↓ deep-merge
provider 2 (e.g., file)
   ↓ deep-merge
provider 3 (e.g., system.config)
   ↓ deep-merge
overrides (caller-supplied last)
   = effective config

Each layer is a DeepPartial<TConfig>. The merge is recursive on objects but replaces arrays and primitives wholesale. The order is fixed: defaults → profile → providers (in declaration order) → overrides.

The function

ts
import { resolveConfiguredPackageConfig } from '@open-matrix/core';

const config = await resolveConfiguredPackageConfig<MyConfig>({
  defaults,                                            // required
  contract: matrixJson.config,                         // optional
  profile: { logLevel: 'debug' },                      // optional
  overrides: { auth: { provider: 'google' } },         // optional
  packageName: '@open-matrix/my-package',
  options: {
    context,                                           // for system.config service
    env: process.env,
    loadFileConfig: async (path) => readJson(path),
    realm: 'COM.EXAMPLE',
    timeoutMs: 5000,
  },
});

The function is async because service providers may issue RPCs. With only env-vars and a defaults JSON, the resolution is effectively synchronous.

Conventional layout in a package

packages/my-package/
├── matrix.json                  # references config/* paths
├── config/
│   ├── config.schema.json       # JSON Schema
│   └── config.defaults.json     # baked-in defaults
└── src/
    └── runtime/
        └── load-config.ts       # calls resolveConfiguredPackageConfig

Most packages do not need a config block — single-purpose actors with no tunables ship without one. Reach for the resolver only when configuration actually exists.

See also

Source: projects/matrix-3/packages/core/src/runtime/PackageConfigResolver.ts:1-349.