@crewhaus/data-retention-engine 0.2.4 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crewhaus/data-retention-engine",
3
- "version": "0.2.4",
3
+ "version": "0.3.1",
4
4
  "type": "module",
5
5
  "description": "GDPR-shaped data retention: retain / export / purge with cron-style sweeper + audit window override (Section 39)",
6
6
  "main": "dist/index.js",
@@ -15,7 +15,7 @@
15
15
  "test": "bun test src"
16
16
  },
17
17
  "dependencies": {
18
- "@crewhaus/errors": "0.2.4"
18
+ "@crewhaus/errors": "0.3.1"
19
19
  },
20
20
  "license": "Apache-2.0",
21
21
  "author": {
@@ -35,5 +35,5 @@
35
35
  "publishConfig": {
36
36
  "access": "public"
37
37
  },
38
- "files": ["dist", "README.md", "LICENSE", "NOTICE"]
38
+ "files": ["dist", "templates", "README.md", "LICENSE", "NOTICE"]
39
39
  }
@@ -0,0 +1,30 @@
1
+ # data-retention-engine templates
2
+
3
+ Copy-in starting points for operating Section 39 GDPR/TTL retention on a
4
+ schedule. These are **templates, not active workflows** — nothing in this
5
+ directory runs from inside the factory repo.
6
+
7
+ - [`retention-sweep.yml`](retention-sweep.yml) — GitHub Actions cron
8
+ template. Copy it into your deployment repository's `.github/workflows/`
9
+ and edit the `<PLACEHOLDER>` comments. It chains the schedulable CLI
10
+ surfaces this package pairs with:
11
+
12
+ 1. `crewhaus retention sweep [--dry-run] [--dir <root>]` — enforce
13
+ `.crewhaus/retention.json` (per-store `maxAgeDays`, session `pins`,
14
+ deferring `auditWindows`) over the harness stores. Sessions expire by
15
+ file mtime under session-store's own eviction rules; the hash-chained
16
+ audit log is **never deleted** (export-only — any deletion breaks
17
+ `verify()`'s cross-file chain). Real runs append a
18
+ `retention_enforcement` evidence record to `.crewhaus/audit`.
19
+ 2. `crewhaus audit verify --dir .crewhaus/audit` — post-sweep tamper
20
+ tripwire over the same chain the sweep just appended to.
21
+
22
+ The companion verbs share the sweep's rules (and all honor `--dry-run`):
23
+ `crewhaus retention export <outDir> [--since <date>]` (right-to-export —
24
+ copies records out, raw) and `crewhaus retention purge [--before <date>]`
25
+ (right-to-delete).
26
+
27
+ **Restoring stores onto a runner? Preserve mtimes.** Retention is
28
+ mtime-keyed, and `aws s3 sync`-style restores stamp files with the
29
+ download time — the sweep then silently never fires. Use the tar-based
30
+ round-trip shown in the template's `<STORE>` comments.
@@ -0,0 +1,98 @@
1
+ # retention-sweep.yml — scheduled GDPR/TTL retention enforcement TEMPLATE.
2
+ #
3
+ # This is a TEMPLATE, not an active workflow: copy it into your deployment
4
+ # repository's `.github/workflows/` and edit the <PLACEHOLDER> comments.
5
+ # One cron line then enforces `.crewhaus/retention.json` over the harness
6
+ # stores on a schedule — expired sessions are deleted under the exact rules
7
+ # session-store's own TTL eviction uses, while the hash-chained audit log is
8
+ # NEVER deleted (any deletion, even a whole oldest day file, would break
9
+ # `crewhaus audit verify`'s tamper check — audit is export-only). Every real
10
+ # sweep appends a `retention_enforcement` record to `.crewhaus/audit`, so the
11
+ # enforcement itself is tamper-evidenced.
12
+ #
13
+ # Prerequisites:
14
+ # - The runner can reach the harness directory that owns `.crewhaus/`
15
+ # (sessions + audit). Stores live where your agents RUN, so on an
16
+ # ephemeral CI runner you typically restore/mount them first — AND
17
+ # persist them back after the sweep, or the deletions (and the appended
18
+ # evidence record) evaporate with the runner (see the <STORE> steps).
19
+ # - Optional: a `.crewhaus/retention.json` in the harness directory. Without
20
+ # one the sweep uses the safe defaults (sessions expire after 30 days —
21
+ # session-store's TTL — no pins, no audit windows):
22
+ # {
23
+ # "version": 1,
24
+ # "sessions": { "maxAgeDays": 30 },
25
+ # "pins": ["sess_0123456789abcdef"],
26
+ # "auditWindows": [
27
+ # { "frameworkId": "soc2", "controlId": "CC6.1",
28
+ # "expiresAt": "2026-08-01T00:00:00Z" }
29
+ # ]
30
+ # }
31
+
32
+ name: retention-sweep
33
+
34
+ on:
35
+ schedule:
36
+ # <CRON> Daily at 04:23 UTC. The sweep is idempotent — anything already
37
+ # deleted or still inside its retention window is simply reported as
38
+ # kept — so a more (or less) frequent cron is equally safe.
39
+ - cron: "23 4 * * *"
40
+ # Manual runs for right-to-delete requests (pair with `retention export`
41
+ # first when the data subject also asked for a copy).
42
+ workflow_dispatch: {}
43
+
44
+ jobs:
45
+ sweep:
46
+ runs-on: ubuntu-latest
47
+ steps:
48
+ - uses: actions/checkout@v4
49
+
50
+ # <STORE> Make the harness `.crewhaus/` stores available to this job.
51
+ # Replace with however your deployment persists them (restore from
52
+ # object storage, mount a volume, run on the host that owns them, ...).
53
+ # Skip it entirely if this workflow already runs on that machine.
54
+ #
55
+ # WARNING — mtimes must survive the round-trip. Session retention is
56
+ # keyed on each file's filesystem MTIME (the same rule session-store's
57
+ # own TTL eviction uses), and `aws s3 sync` (like most object-store
58
+ # download tools) stamps restored files with the DOWNLOAD time — every
59
+ # session then looks freshly touched and the sweep silently never
60
+ # deletes anything on a restored runner. Persist/restore an archive
61
+ # that preserves mtimes instead, e.g. tar (which records and restores
62
+ # them by default):
63
+ # persist: tar -czf crewhaus-store.tgz .crewhaus
64
+ # aws s3 cp crewhaus-store.tgz s3://<YOUR-BUCKET>/
65
+ # restore: aws s3 cp s3://<YOUR-BUCKET>/crewhaus-store.tgz .
66
+ # tar -xpzf crewhaus-store.tgz
67
+ # - name: Restore harness stores
68
+ # run: aws s3 sync s3://<YOUR-BUCKET>/crewhaus-store .crewhaus
69
+
70
+ - name: Install crewhaus
71
+ # Any channel works: npm i -g crewhaus, brew install crewhaus/tap/crewhaus, ...
72
+ run: npm install -g crewhaus
73
+
74
+ - name: Dry-run first — log what the sweep will delete and why
75
+ # Deletes nothing and appends no evidence; the report lands in the
76
+ # job log so a surprising deletion set is visible BEFORE it happens.
77
+ run: crewhaus retention sweep --dry-run
78
+
79
+ - name: Enforce retention
80
+ # Deletes expired sessions (honoring retention.json pins + audit
81
+ # windows; audit day files are never deleted) and appends the
82
+ # tamper-evident `retention_enforcement` record to .crewhaus/audit.
83
+ run: crewhaus retention sweep
84
+
85
+ - name: Verify the audit chain still checks out
86
+ # The sweep's evidence append must land on an intact chain — a broken
87
+ # link here means the store was tampered with out-of-band.
88
+ run: crewhaus audit verify --dir .crewhaus/audit
89
+
90
+ # <STORE> Persist the swept stores back if you restored them above —
91
+ # otherwise the deletions AND the evidence record are lost with the
92
+ # runner and the next sweep re-reports the same expired sessions.
93
+ # Use the mtime-preserving tar round-trip from the restore step above,
94
+ # NOT a bare `aws s3 sync` (see the WARNING there).
95
+ # - name: Persist harness stores
96
+ # run: |
97
+ # tar -czf crewhaus-store.tgz .crewhaus
98
+ # aws s3 cp crewhaus-store.tgz s3://<YOUR-BUCKET>/crewhaus-store.tgz