Skip to main content
Version: Next

exchange Package

import "github.com/croessner/nauthilus/v3/pluginapi/v1/exchange"

The package defines a conventional runtime keyspace shared by native and compatible plugin features. Builders return JSON/runtime-compatible maps and pluginapi.RuntimeDelta values; readers create defensive views for analytics and cross-plugin consumption.

Runtime key constants

ConstantExact valueIntended value
Prefixplugin.exchange.Top-level standard exchange prefix.
KeyDecisionSourcesplugin.exchange.decision_sourcesExplicit source names, accepted by StringList.
KeyFeaturePrefixplugin.exchange.feature.Prefix followed by a feature name and containing a marker map.
KeyGeoIPplugin.exchange.geoipGeoIP/ASN enrichment map.
KeyGeoIPReputationplugin.exchange.geoip_reputationReputation score/decision map.
KeyFailedLoginHotspotplugin.exchange.failed_login_hotspotFailed-login hotspot map.
KeyAccountProtectionplugin.exchange.account_protectionAccount-protection state map.
KeyGlobalPatternplugin.exchange.global_patternGlobal pattern counters/map.
KeyDynamicResponseplugin.exchange.dynamic_responseDynamic-response diagnostics map.
KeyHaveIBeenPwnedplugin.exchange.haveibeenpwndSecret-safe HIBP result map.

These constants define conventions, not capability gates. Runtime delta validation still applies, and another component can overwrite/delete keys later.

Feature name constants

ConstantExact value
FeatureBlocklistblocklist
FeatureRBLrbl
FeatureBruteForcebrute_force
FeatureGeoIPPolicygeoip_policyd
FeatureFailedLoginHotspotfailed_login_hotspot
FeatureAccountProtectionaccount_protection
FeatureGeoIPReputationgeoip_reputation
FeatureGlobalPatternglobal_pattern
FeatureDynamicResponsedynamic_response
FeatureHaveIBeenPwnedhaveibeenpwnd

The package uses this table order when it enumerates standard feature-marker sources. Unknown feature names follow in lexical order.

Field constants

ConstantExact valueMeaning
FieldTriggeredtriggeredMarker contributed to the decision.
FieldDecisiondecisionBounded hint such as suspicious.
FieldSourcesourceLow-cardinality producer/data-source name.
FieldReasonreasonOptional bounded reason.
FieldHashInfohash_infoHIBP short prefix/count information used by analytics. It must remain secret-safe.
FieldLeakedleakedHIBP leak boolean.
FieldCountcountNon-negative HIBP leak count.

FeatureMarker

type FeatureMarker struct {
Triggered bool
Decision string
Source string
Reason string
}

Triggered is included in a built value only when true. Decision, Source, and Reason are trimmed and included only when non-empty. Builders do not enforce length/cardinality or a decision enum.

Feature builders

FeatureKey

func FeatureKey(name string) string

Returns KeyFeaturePrefix + strings.TrimSpace(name). Empty input therefore returns the prefix itself; use FeatureRuntimeDelta when you want empty names rejected.

FeatureMarkerValue

func FeatureMarkerValue(marker FeatureMarker) map[string]any

Returns a new map containing the non-zero marker fields under the field constants above. False/blank fields are omitted; the result can be empty.

FeatureRuntimeDelta

func FeatureRuntimeDelta(name string, marker FeatureMarker) pluginapi.RuntimeDelta

Trims name. Empty returns a zero delta. Otherwise it builds the marker map and returns a delta only when that map is non-empty; the key is plugin.exchange.feature.<name>. It does not validate the feature name against the predefined constants.

HIBPResult

type HIBPResult struct {
HashInfo string
Leaked *bool
Count *uint64
}

HashInfo is trimmed and omitted when empty. Pointer fields distinguish absent from explicit false/zero. The type name does not make arbitrary HashInfo content safe: never put a full password hash or password-derived secret in it.

HIBP builders

HIBPValue

func HIBPValue(result HIBPResult) map[string]any

Returns a new map. It includes non-empty trimmed HashInfo, and includes Leaked/Count whenever their pointers are non-nil, including false and zero.

HIBPRuntimeDelta

func HIBPRuntimeDelta(result HIBPResult) pluginapi.RuntimeDelta

Stores the non-empty HIBPValue under KeyHaveIBeenPwned; an entirely empty value yields a zero delta.

GeoIP builders

GeoIPValue

func GeoIPValue(fields map[string]any) map[string]any

Returns a defensive copy using the package's supported-container clone rules described below. It does not prescribe or validate field names/types.

GeoIPRuntimeDelta

func GeoIPRuntimeDelta(fields map[string]any) pluginapi.RuntimeDelta

Stores a non-empty copied map at KeyGeoIP; empty input yields a zero delta.

GeoIPReputationValue

func GeoIPReputationValue(fields map[string]any) map[string]any

Defensive-copy equivalent for reputation data.

GeoIPReputationRuntimeDelta

func GeoIPReputationRuntimeDelta(fields map[string]any) pluginapi.RuntimeDelta

Stores a non-empty copied map at KeyGeoIPReputation; empty input yields a zero delta.

Snapshot

type Snapshot struct {
// unexported values and fact indexes
}

Snapshot has no exported fields. Its zero value is safe for reads: missing values produce nil/empty outputs.

NewSnapshot

func NewSnapshot(runtime pluginapi.RuntimeContext, facts []pluginapi.PolicyFact) Snapshot

Calls runtime.Snapshot() when non-nil, otherwise uses an empty map, then delegates to NewSnapshotFromValues.

NewSnapshotFromValues

func NewSnapshotFromValues(values map[string]any, facts []pluginapi.PolicyFact) Snapshot

Copies supported runtime containers and indexes recognized policy facts by feature namespace. It recognizes:

lua.plugin.<namespace>.<field...>
plugin.<producer>.<namespace>.<field...>

For the native form, <producer> is skipped; namespace is the next segment. Attributes with too few segments or another prefix are ignored. Deeper field segments are rejoined with dots. If facts repeat the same namespace/field, the later fact wins.

Snapshot.Value

func (s Snapshot) Value(key string) any

Returns a clone of the raw runtime value at key; a missing key returns nil. No prefix restriction is applied.

Snapshot.Map

func (s Snapshot) Map(key string) map[string]any

Converts/copies a map-like runtime value through MapValue; unsupported or missing values return nil.

Snapshot.HIBPHashInfo

func (s Snapshot) HIBPHashInfo() string

Reads FieldHashInfo from the KeyHaveIBeenPwned map and converts it through StringValue. No policy-fact fallback is used.

Snapshot.GeoIPReputation

func (s Snapshot) GeoIPReputation() map[string]any

Returns non-empty runtime KeyGeoIPReputation first. If absent/empty, returns a copy of policy-fact fields indexed under namespace geoip_reputation.

Snapshot.DecisionSources

func (s Snapshot) DecisionSources() []string

Returns deterministic, first-seen, exact-string-deduplicated source names in this collection order:

  1. explicit KeyDecisionSources normalized by StringList;
  2. triggered feature-marker keys, known features in the constant order above, then unknown features lexically;
  3. implied exchange sources:
    • KeyGeoIP["rejected"] -> geoip_policyd;
    • KeyFailedLoginHotspot["triggered"] -> failed_login_hotspot;
    • KeyAccountProtection["active"] -> account_protection;
    • case-insensitive trimmed KeyGeoIPReputation["decision"] == "suspicious" -> geoip_reputation;
  4. implied policy-fact sources:
    • blocklist.matched -> blocklist;
    • geoip.rejected -> geoip_policyd;
    • failed_login_hotspot.triggered;
    • account_protection.active;
    • suspicious geoip_reputation.decision.

Feature markers add their feature name only when triggered is truthy. Dedupe preserves the first occurrence and is case-sensitive. Blank source names are skipped.

Snapshot.DecisionSourcesString

func (s Snapshot) DecisionSourcesString() string

Joins DecisionSources() with commas and no spaces. Empty sources produce "". Do not use this unbounded analytics field as a metric label without an explicit finite allowlist.

Conversion functions

MapValue

func MapValue(value any) map[string]any

Accepted inputs:

  • map[string]any: cloned with supported-container rules;
  • map[string]string: converted to map[string]any;
  • map[string]bool: converted to map[string]any;
  • nil or anything else: nil.

StringValue

func StringValue(value any) string

Nil becomes ""; strings are returned unchanged; fmt.Stringer uses String; everything else uses fmt.Sprint. It does not trim, bound, redact, or reject container values.

StringList

func StringList(value any) []string
InputResult
[]stringTrim each item, drop blanks, preserve order and duplicates.
[]anyConvert each item with StringValue, trim/drop blanks, preserve order and duplicates.
map[string]anySelect keys whose value is Truthy, trim/drop blank keys, sort lexically.
map[string]boolSelect true keys, trim/drop blanks, sort lexically.
other/nilnil.

Truthy

func Truthy(value any) bool

Returns true only for boolean true or a string equal to true after trimming and case-folding. Boolean false and string false return false. Numbers, non-boolean strings, and all other types return false; non-zero numbers are not truthy.

Copy boundary

The package recursively copies map[string]any, []any, and []string. It converts top-level map[string]string and map[string]bool in MapValue. Other nested reference-shaped values are returned as-is by the generic clone path. Therefore NewSnapshotFromValues is not a universal deep-copy function for arbitrary custom maps/slices/pointers. Runtime values obtained from the host's normalized RuntimeContext are the intended safe input; callers supplying raw values must restrict them to supported JSON-like containers or make their own deep copy.