Skip to main content
Version: Next

Generic Policy Providers

Nauthilus v4 native plugins extend the shared Decision Service through two additive interfaces:

type DecisionFactProvider interface {
Descriptor() DecisionFactProviderDescriptor
Collect(context.Context, DecisionFactRequest) (DecisionFactResult, error)
}

type DecisionEffectProvider interface {
Descriptor() DecisionEffectProviderDescriptor
Execute(context.Context, DecisionEffectRequest) (DecisionEffectResult, error)
}

A fact provider contributes declared facts. It does not select decisions, schedule itself, or execute effects. An effect provider runs only an effect already selected and invoked by the host. Registration advertises capability; operator configuration and a prepared Policy generation activate exact bindings.

Register implementations through the optional DecisionRegistrar supplied to the plugin registration boundary:

type DecisionRegistrar interface {
RegisterDecisionFactProvider(DecisionFactProvider) error
RegisterDecisionEffectProvider(DecisionEffectProvider) error
}

Fact-provider descriptors

type DecisionFactProviderDescriptor struct {
Targets []DecisionTargetSelector
Outputs []DecisionFactOutputDescriptor
Namespace string
Name string
Timeout time.Duration
}

Namespace and Name are canonical lowercase component identities. Targets is a non-empty exact list of namespace/action selectors; wildcards are not accepted. Outputs declares every provider-local fact name, category, kind, and applicable bound. Timeout must be positive and cannot exceed MaximumDecisionFactProviderTimeout (10 minutes); configured execution may narrow it.

type DecisionFactOutputDescriptor struct {
Name string
Category DecisionFactCategory
Kind DecisionValueKind
MaxLength int
MaxItems int
MaxBytes int
}

Categories are subject, resource, and environment. The host qualifies the local output with the configured namespace and provider identity. Return only descriptor-declared names with matching kinds and bounds.

Strict values and ordered records

DecisionValue is a deeply owned one-of value constructed with NewDecisionValue. Exactly one input member must be active, including when a list, byte slice, or record list is intentionally empty.

KindConstructor inputNotes
stringString *stringValid UTF-8; descriptor/schema length bound applies.
booleanBoolean *boolfalse is a present value.
integerInteger *int64Signed 64-bit integer.
doubleDouble *float64NaN and infinity are rejected.
stringsStrings []stringOrdered valid UTF-8 list; non-nil empty differs from absent.
bytesBytes []byteOwned bytes; non-nil empty differs from absent.
timestampTimestamp *time.TimeNormalized to UTC.
recordsRecords *DecisionRecordListOrdered schema-bound records.

Record lists are built with NewDecisionRecordFieldValue, NewDecisionRecordField, NewDecisionRecord, and NewDecisionRecordList. Each record is a non-empty ordered field slice with unique canonical local field names. Record fields can contain scalar, string-list, bytes, or timestamp values but never another record list. The target schema supplies record count, field count, aggregate bytes, per-field kinds/bounds, required fields, visibility, and predicate rules.

Policy record predicates select one field and apply configured any or all quantification. Missing, malformed, empty, and schema-incompatible collections are distinct states; none is silently coerced into a vacuous permit.

Fact collection

DecisionFactRequest exposes immutable copies of:

  • Target() — the exact namespace/action selected by the host;
  • Caller() — redacted principal, client ID, authentication kind, and normalized Policy scopes;
  • Facts() — admitted host fact views visible to this provider.

DecisionFactView contains only the canonical fact ID, category, and strict value. It omits credentials, provenance controls, scheduling controls, and mutable host state.

Return facts or one safe failure class:

type DecisionFactResult struct {
Facts []DecisionFactOutput
ErrorClass DecisionErrorClass
}

Failure classes are invalid_input, unavailable, timeout, and internal. Errors and result classes must not carry dependency responses, credentials, raw requests, or unbounded text. Honor the context deadline and cancellation; do not start detached work.

Effect-provider descriptors

type DecisionEffectProviderDescriptor struct {
Effects []DecisionEffectDescriptor
Namespace string
Name string
}

Each effect declares a provider-local name, exact targets, execution boundary, and bounded typed parameters:

type DecisionEffectDescriptor struct {
Targets []DecisionTargetSelector
Parameters []DecisionEffectParameterDescriptor
Name string
Execution DecisionEffectExecution
}

Execution is either host_sync (before response finalization) or host_post_action (accepted by the host supervisor and executed asynchronously). Parameter kinds use the non-record value vocabulary; a parameter may be required, non-empty, length/item/byte bounded, and restricted to an allowed string set.

DecisionEffectRequest provides the exact target, selected local effect, immutable parameters, redacted caller, and visible fact views. It provides no retry, replay, lifecycle, scheduling, or decision-selection control.

Return one of succeeded, failed, or outcome_unknown, plus an optional safe failure class. Use outcome_unknown only when an external dispatch may have happened but completion cannot be established. The host attempts a selected effect ordinal at most once and does not provide automatic replay or an outcome query; external reconciliation must use a provider-owned domain identifier.

Minimal provider shape

type riskProvider struct{}

func (riskProvider) Descriptor() pluginapi.DecisionFactProviderDescriptor {
return pluginapi.DecisionFactProviderDescriptor{
Namespace: "mail.security",
Name: "risk",
Targets: []pluginapi.DecisionTargetSelector{
{Namespace: "authn", Action: "authenticate"},
},
Outputs: []pluginapi.DecisionFactOutputDescriptor{
{
Name: "risk.score",
Category: pluginapi.DecisionFactCategoryEnvironment,
Kind: pluginapi.DecisionValueKindString,
MaxLength: 16,
},
},
Timeout: time.Second,
}
}

The exact canonical provider identity is assigned by the host after module registration and Policy configuration. See Native Go Plugins, runtime and effects, and the reference plugins for complete activation examples.