Skip to content

feat(rules): layered default/ + user/ policy directories#61

Merged
CharmingGroot merged 1 commit into
release/v0.1from
feat/layered-rules
Jul 22, 2026
Merged

feat(rules): layered default/ + user/ policy directories#61
CharmingGroot merged 1 commit into
release/v0.1from
feat/layered-rules

Conversation

@CharmingGroot

Copy link
Copy Markdown
Owner

Gap

CLAUDE.md rule 2 asks to separate project-managed default/ rules (overwritten on upgrade) from user/ customization (preserved) — but the code only loaded a single YAML file. Rule↔implementation gap; and for an OSS tool it's the standard packaging concern (upgrades must not clobber user rules).

Design (generic, no hardcoded paths)

  • pasu-rules (pure lib, locally tested):
    • Ruleset::from_dir(dir) — loads every *.yaml/*.yml sorted by filename (the 10-…,20-… convention, like Falco rules.d / sudoers.d). Missing dir → empty fail-closed ruleset; default is deny unless every file declares allow.
    • Ruleset::layered(base, user) — user overlay's rules go first (first-match precedence); default merged deny-wins (fail-closed).
  • pasu-daemon: --policy-dir <dir> layers <dir>/user/ over <dir>/default/. Paths come from the CLI — never hardcoded in the library. --policy <file> still works; the two are mutually exclusive (exactly one required).

Tests

  • pasu-rules (local): user-overrides-baseline, deny-wins default, sorted concat + missing-dir fail-closed.
  • pasu-daemon: policy-dir layering (baseline 1.1.1.1 + user 9.9.9.9 both lowered), exactly-one-source required.

CLAUDE.md rule 2 asks to separate project-managed `default/` rules (overwritten
on upgrade) from `user/` customization (preserved), but the code only loaded a
single YAML file — a rule↔implementation gap.

pasu-rules (generic, no hardcoded paths):
- `Ruleset::from_dir(dir)` loads every `*.yaml`/`*.yml` sorted by filename (the
  `10-…`, `20-…` convention, like Falco's rules.d / sudoers.d); missing dir →
  empty fail-closed ruleset; default is deny unless every file declares allow.
- `Ruleset::layered(base, user)` puts the user overlay's rules first (first-match
  precedence) and merges the default deny-wins (fail-closed).

pasu-daemon: `--policy-dir <dir>` layers `<dir>/user/` over `<dir>/default/`;
paths come from the CLI, never hardcoded in the library. `--policy <file>` still
works; the two are mutually exclusive (exactly one required).

Tests (pasu-rules, run locally): user-overrides-baseline, deny-wins default,
sorted dir concat + missing-dir fail-closed; (pasu-daemon) policy-dir layering +
exactly-one-source required.

Signed-off-by: Ho Geun Choi <ohyes9711@gmail.com>
@CharmingGroot
CharmingGroot merged commit 25e7872 into release/v0.1 Jul 22, 2026
4 checks passed
@CharmingGroot
CharmingGroot deleted the feat/layered-rules branch July 22, 2026 04:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant