1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// Location: ./crates/apl-core/src/attribute_source.rs
// Copyright 2026
// SPDX-License-Identifier: Apache-2.0
// Authors: Teryl Taylor
//
// Static attribute provisioning — the `data.*` bag namespace.
//
// `restrict` predicates (and policy predicates generally) read attributes
// that aren't carried by any token or fetched from anywhere: backend-free
// policy constants like tenant→region maps, per-agent model allow-lists,
// org defaults. Those come from a plain, operator-organized data tree that
// lands in the evaluation bag under `data.*`.
//
// This module is the pure contract: the `AttributeSource` trait (where a
// tree comes from) and the `AttributeTree` value (what it is). The default
// file-backed source and the bag flattening live at the outer layers
// (apl-cpex / apl-cmf) — apl-core stays free of I/O and config deps.
use Value;
use Error;
/// The static attribute tree — the whole `data:` document, a plain nested
/// value the operator organizes however they like. It carries *literal
/// values only*: no conditionals, no computed fields (that guardrail is
/// structural — there is no syntax in a data tree to express logic).
///
/// Flattened into the bag under `data.*` by the bag builder (apl-cmf):
/// `{ org: { default_region: us } }` → `data.org.default_region = "us"`.
;
/// Where the `data.*` tree comes from — a **trait object injected at
/// construction**, not a CPEX hook-plugin. The host implements it over a
/// file, etcd, Postgres, a k8s ConfigMap, etc., and hands the object to
/// the runtime at startup.
///
/// `load` is **synchronous and one-shot**: it runs once at startup, never
/// on the request hot path, so blocking the init thread on file/network
/// I/O is fine — and it lets the (synchronous) runtime setup call it
/// directly, no async plumbing. A source that fronts a genuinely async
/// store can block on its own runtime at this edge.
///
/// v1 is **snapshot only**. Hot-reload (a `watch` that streams fresh
/// trees) is deferred — that is the one genuinely-async concern, and its
/// shape (stream vs channel vs callback) is best decided when it's built,
/// not stubbed now. A lazy per-key `resolve(path)` for huge stores is
/// likewise deferred.
/// Why an attribute source failed to produce a tree. Loading is a
/// startup/config concern, so these surface as configuration errors at
/// the runtime boundary (fail-fast, not per-request).