Phase 2 enhancements to the policy decider, plus listing-level ETags
that benefit every deployment regardless of decider mode.
Reference Rego policy
---------------------
internal/policy/rego/access.rego mirrors InternalDecider's semantics
exactly — bottom-up walk, deny-first within a level, default-deny when
HasAnyFile=true, glob matching with @-boundary semantics (special-cased
bare "*" because OPA's glob.match treats empty delimiters
inconsistently for that pattern).
Embedded into the binary via go:embed; --print-rego dumps it to stdout
so federal customers standing up an external OPA can use it as a
parity-tested baseline:
zddc-server --print-rego > /etc/opa/policies/zddc-access.rego
Parity test runner
------------------
parity_test.go imports the OPA Go module as a TEST-ONLY dependency
(github.com/open-policy-agent/opa@v0.70.0). Every fixture from the
internal Go evaluator's test set runs through both implementations;
any divergence fails CI. The test-only import means production
binaries (built by `go build ./cmd/zddc-server`) stay OPA-free —
release-flag binary size unchanged at ~13 MB.
The parity test caught a real bug on first run: bare "*" patterns
didn't match through OPA's glob.match with empty delimiters. Fixed
in access.rego with a special-case rule. This is exactly the kind of
subtle drift the parity guard exists to catch.
External-mode decision cache
----------------------------
HTTPDecider is now wrapped in a cachingDecider with a default 1s TTL.
Bursty patterns like .archive listings (one OPA round-trip per entry
before, one per (email, decision-input) tuple per TTL window after)
amortize cleanly. Verified: 20 identical /D/ requests produce 1 OPA
hit with cache, 40 hits without (each listing makes 2 ACL queries).
ZDDC_OPA_CACHE_TTL knob (default 1s) lets operators tune. 0 disables.
1s matches the fsnotify watcher debounce window — staleness is
bounded the same way other policy-edit propagation already is.
Internal mode unchanged; the in-process Go evaluator is already
cheaper than a cache lookup would be.
Listing ETags
-------------
GET / (project list) and GET /<dir>/ (directory listing JSON) now
carry content-hash ETag + Cache-Control: private, max-age=0,
must-revalidate. SHA-256 of the rendered JSON, truncated to 16 hex
chars (64 bits — collision risk on a listing of any realistic size
is vanishingly small).
Server-side caching deliberately not added: it would require
mtime-based invalidation, and Azure Files SMB mounts (a common
deployment substrate) don't support fsnotify reliably. The
content-hash ETag delivers the bandwidth savings (304 on identical
fetches) without depending on watcher correctness — the hash is the
actual response, so it can't lie about staleness regardless of
underlying watcher behavior.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
44 lines
1.8 KiB
Modula-2
44 lines
1.8 KiB
Modula-2
module codeberg.org/VARASYS/ZDDC/zddc
|
|
|
|
go 1.24
|
|
|
|
require (
|
|
github.com/fsnotify/fsnotify v1.9.0
|
|
github.com/klauspost/compress v1.18.6
|
|
github.com/open-policy-agent/opa v0.70.0
|
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
|
gopkg.in/yaml.v3 v3.0.1
|
|
)
|
|
|
|
require (
|
|
github.com/OneOfOne/xxhash v1.2.8 // indirect
|
|
github.com/agnivade/levenshtein v1.2.0 // indirect
|
|
github.com/beorn7/perks v1.0.1 // indirect
|
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
|
github.com/go-ini/ini v1.67.0 // indirect
|
|
github.com/go-logr/logr v1.4.2 // indirect
|
|
github.com/go-logr/stdr v1.2.2 // indirect
|
|
github.com/gobwas/glob v0.2.3 // indirect
|
|
github.com/google/uuid v1.6.0 // indirect
|
|
github.com/gorilla/mux v1.8.1 // indirect
|
|
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
|
github.com/prometheus/client_golang v1.20.5 // indirect
|
|
github.com/prometheus/client_model v0.6.1 // indirect
|
|
github.com/prometheus/common v0.55.0 // indirect
|
|
github.com/prometheus/procfs v0.15.1 // indirect
|
|
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 // indirect
|
|
github.com/sirupsen/logrus v1.9.3 // indirect
|
|
github.com/tchap/go-patricia/v2 v2.3.1 // indirect
|
|
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
|
github.com/yashtewari/glob-intersection v0.2.0 // indirect
|
|
go.opentelemetry.io/otel v1.28.0 // indirect
|
|
go.opentelemetry.io/otel/metric v1.28.0 // indirect
|
|
go.opentelemetry.io/otel/sdk v1.28.0 // indirect
|
|
go.opentelemetry.io/otel/trace v1.28.0 // indirect
|
|
golang.org/x/sys v0.26.0 // indirect
|
|
google.golang.org/protobuf v1.34.2 // indirect
|
|
sigs.k8s.io/yaml v1.4.0 // indirect
|
|
)
|