Skip to content

Credential references

A credential reference is anything that lets a caller use a credential without seeing its bytes. The Matrix pattern is a two-step lease:

  1. Caller asks Factotum for a leaseId.
  2. A trusted system actor exchanges the leaseId for HTTP headers ready to send to the provider.

The raw token never crosses the bus.

The two ops

Source: projects/matrix-3/packages/system-factotum/dist/index.js.

credential.lease

ts
// system-factotum/dist/index.js:557-587
async onCredentialLease(payload): Promise<Result> {
  if (!payload.provider) return { ok: false, error: 'provider is required' };

  const credential = this._fileStore.getProvider(payload.provider);
  if (!credential) {
    this.emit('factotum.needkey', { provider: payload.provider, requestedBy: 'unknown' });
    return { ok: false, error: `No credential found for provider: ${payload.provider}` };
  }

  const leaseId = this._generateLeaseId();
  const expiresAt = Date.now() + LEASE_TTL_MS;     // 5 minutes
  this._leases.set(leaseId, {
    provider: payload.provider,
    scope: payload.scope ?? 'infer',
    rootId: ctx?.rootId ?? 'local',
    createdAt: Date.now(),
    expiresAt,
  });

  return {
    ok: true,
    leaseId,                   // 32-hex string
    provider: payload.provider,
    type: credential.type,     // 'oauth' | 'api-key' | 'local'
    expiresAt,
    scope,
  };
}

The lease is recorded in an in-memory map. It expires in 5 minutes (LEASE_TTL_MS). A cleanup timer prunes expired leases every minute.

credential.materialize

ts
// system-factotum/dist/index.js:593-628
async onCredentialMaterialize(payload): Promise<Result> {
  if (!payload.leaseId) return { ok: false, error: 'leaseId is required' };

  const lease = this._leases.get(payload.leaseId);
  if (!lease) return { ok: false, error: 'Lease not found or expired' };
  if (Date.now() > lease.expiresAt) {
    this._leases.delete(payload.leaseId);
    return { ok: false, error: 'Lease expired' };
  }

  const credential = this._fileStore.getProvider(lease.provider);
  if (!credential) {
    this._leases.delete(payload.leaseId);
    return { ok: false, error: 'Credential no longer available' };
  }

  const headers = {};
  let baseUrl;

  if (credential.type === 'api-key' && credential.apiKey) {
    if (lease.provider === 'anthropic') {
      headers['x-api-key'] = credential.apiKey;
      headers['anthropic-version'] = '2023-06-01';
    } else if (lease.provider === 'openai' || lease.provider === 'codex') {
      headers['Authorization'] = `Bearer ${credential.apiKey}`;
    } else {
      headers['Authorization'] = `Bearer ${credential.apiKey}`;
    }
  } else if (credential.type === 'oauth' && credential.accessToken) {
    headers['Authorization'] = `Bearer ${credential.accessToken}`;
  } else if (credential.type === 'local' && credential.baseUrl) {
    baseUrl = credential.baseUrl;
  }

  this._leases.delete(payload.leaseId);   // ONE-SHOT consumption
  return { ok: true, headers, baseUrl };
}

Key properties:

  • The materialized headers are returned exactly once. Calling credential.materialize with the same leaseId again returns { ok: false, error: 'Lease not found or expired' }.
  • The lease is consumed regardless of HTTP success at the provider. Failed provider calls require a fresh lease.
  • The materialize op is documented as "ONLY callable by trusted system actors (system.inference)." Today this is by convention; the framework does not enforce a caller allow-list. Auditing target state would add an explicit check.

Caller pattern

ts
// inside an inference adapter
const lease = await RequestReply.execute(this.context,
  'system.factotum', 'credential.lease',
  { provider: 'anthropic', scope: 'infer' },
);
if (!lease.ok) throw new Error(`No credential: ${lease.error}`);

// hand the lease to a trusted system actor (here we're in one)
const materialized = await RequestReply.execute(this.context,
  'system.factotum', 'credential.materialize',
  { leaseId: lease.leaseId },
);
if (!materialized.ok) throw new Error(`Materialize failed: ${materialized.error}`);

// now make the HTTPS call with materialized.headers
const res = await fetch('https://api.anthropic.com/v1/messages', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', ...materialized.headers },
  body: JSON.stringify({ ... }),
});

The raw apiKey / accessToken only exists inside the materialize result and only inside the calling actor's process memory. It does not travel on the bus, does not appear in logs, and does not get serialized into any other state.

Why two ops, not one

You could imagine a single credential.get-headers({ provider }) op. The two-op design has three reasons:

  1. Separation of concerns. lease decides whether a caller can use a credential (today: just "credential exists for this provider"; target state: capability checks). materialize decides how the credential gets formatted into HTTP headers. Two sets of code, two sets of reviewers.
  2. Trust boundary in materialize. materialize is documented as only-for-trusted-system-actors. A future enforcement pass adds the check there without affecting lease callers.
  3. Lease can outlive the lock window. A caller can hold a leaseId across a longer pipeline, materializing only at the point of HTTP call. This makes the raw-key window minimal.

Lease scopes

Today, scope on a lease is recorded but not enforced. Defined values:

ScopeUse
inferinference API calls
adminmanagement operations
read-onlylisting / status only

Target state would intersect these with capability tokens to decide which scopes a caller may request.

Failure modes

SymptomCause
credential.lease → { ok: false, error: 'No credential found for provider: X' }Factotum has no row for provider X. The op also emits factotum.needkey { provider, requestedBy } so a UI can prompt the user.
credential.materialize → { ok: false, error: 'Lease not found or expired' }The lease was already consumed, expired, or never existed.
credential.materialize → { ok: false, error: 'Credential no longer available' }The credential row was deleted between lease and materialize. The lease is consumed.

See also

Source: projects/matrix-3/packages/system-factotum/dist/index.js (onCredentialLease at lines 557-587, onCredentialMaterialize at lines 593-628, lease bookkeeping at 392-409 and 636-643).