You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(natsstore): serve all tenants from a single muxed K/V bucket
Replace the bucket-per-tenant model with one muxed bucket (config.bucket),
keyed "<tenant>.<key...>". The Rego interface is unchanged: data still lands at
data.nats.kv.<tenant>.<...> and the builtins (nats.kv.watch_bucket /
nats.kv.get_data) keep their names and arity — their first argument is now the
tenant token (the leading key segment) instead of a bucket name.
Core changes:
- config: add required `bucket`; rename `root_bucket` -> `root_tenant`
(a tenant whose subtree mounts at the OPA data root).
- NATSKeyToOPAPath: derive the tenant from the key's first token (drop the
out-of-band bucket arg); root mode strips the tenant token.
- nats_client: open the single bucket once (cached); add tenantKeys(), a
prefix-filtered lister so loads are O(tenant), never O(whole bucket).
- watcher: watch "<tenant>.>" (single filter -> scopeable consumer) instead of
">"; identity is the tenant.
- builtins: loadTenantAsGJSON reads only the tenant slice and strips the
"<tenant>." prefix via the new pure buildTenantJSON helper.
Tests (TDD for the pure logic):
- reshaped path-mapping/config/JSON-builder unit tests;
- new muxed_integration_test.go (real NATS): tenantKeys isolation, data
placement, root-tenant stripping;
- migrated the example (one DATA bucket, <tenant>.* keys) + configs; the
docker-compose integration test passes end to end.
No backward compatibility retained (intentional).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
echo 'Populating Administrators group (bucket: 550e8400-e29b-41d4-a716-446655440000)...';
76
-
# In this bucket, store data without the groups.{uuid} prefix since the bucket IS the group
77
-
nats kv put 550e8400-e29b-41d4-a716-446655440000 metadata '{\"id\":\"550e8400-e29b-41d4-a716-446655440000\",\"name\":\"Administrators\",\"description\":\"System administrators group\",\"created_at\":\"2024-01-15T10:00:00Z\"}';
78
-
nats kv put 550e8400-e29b-41d4-a716-446655440000 members.123e4567-e89b-12d3-a456-426614174000 '{\"user_id\":\"123e4567-e89b-12d3-a456-426614174000\",\"joined_at\":\"2024-01-15T10:05:00Z\",\"role\":\"admin\"}';
79
-
nats kv put 550e8400-e29b-41d4-a716-446655440000 members.234e5678-e89b-12d3-a456-426614174001 '{\"user_id\":\"234e5678-e89b-12d3-a456-426614174001\",\"joined_at\":\"2024-01-15T10:10:00Z\",\"role\":\"member\"}';
80
-
nats kv put 550e8400-e29b-41d4-a716-446655440000 permissions.perm-admin-api '{\"id\":\"perm-admin-api\",\"resource\":\"api\",\"action\":\"admin\",\"effect\":\"allow\"}';
81
-
nats kv put 550e8400-e29b-41d4-a716-446655440000 permissions.perm-admin-users '{\"id\":\"perm-admin-users\",\"resource\":\"users\",\"action\":\"manage\",\"effect\":\"allow\"}';
82
-
83
-
echo 'Populating Developers group (bucket: 660e8400-e29b-41d4-a716-446655440001)...';
84
-
nats kv put 660e8400-e29b-41d4-a716-446655440001 metadata '{\"id\":\"660e8400-e29b-41d4-a716-446655440001\",\"name\":\"Developers\",\"description\":\"Development team group\",\"created_at\":\"2024-01-15T10:30:00Z\"}';
85
-
nats kv put 660e8400-e29b-41d4-a716-446655440001 members.345e6789-e89b-12d3-a456-426614174002 '{\"user_id\":\"345e6789-e89b-12d3-a456-426614174002\",\"joined_at\":\"2024-01-15T10:35:00Z\",\"role\":\"lead\"}';
86
-
nats kv put 660e8400-e29b-41d4-a716-446655440001 members.234e5678-e89b-12d3-a456-426614174001 '{\"user_id\":\"234e5678-e89b-12d3-a456-426614174001\",\"joined_at\":\"2024-01-15T10:40:00Z\",\"role\":\"member\"}';
87
-
nats kv put 660e8400-e29b-41d4-a716-446655440001 permissions.perm-dev-code '{\"id\":\"perm-dev-code\",\"resource\":\"code\",\"action\":\"write\",\"effect\":\"allow\"}';
88
-
nats kv put 660e8400-e29b-41d4-a716-446655440001 permissions.perm-dev-deploy '{\"id\":\"perm-dev-deploy\",\"resource\":\"deployment\",\"action\":\"deploy\",\"effect\":\"allow\"}';
89
-
90
-
echo 'Populating ReadOnly Users group (bucket: 770e8400-e29b-41d4-a716-446655440002)...';
91
-
nats kv put 770e8400-e29b-41d4-a716-446655440002 metadata '{\"id\":\"770e8400-e29b-41d4-a716-446655440002\",\"name\":\"ReadOnly Users\",\"description\":\"Read-only access group\",\"created_at\":\"2024-01-15T11:00:00Z\"}';
92
-
nats kv put 770e8400-e29b-41d4-a716-446655440002 members.456e7890-e89b-12d3-a456-426614174003 '{\"user_id\":\"456e7890-e89b-12d3-a456-426614174003\",\"joined_at\":\"2024-01-15T11:05:00Z\",\"role\":\"member\"}';
93
-
nats kv put 770e8400-e29b-41d4-a716-446655440002 permissions.perm-readonly-data '{\"id\":\"perm-readonly-data\",\"resource\":\"data\",\"action\":\"read\",\"effect\":\"allow\"}';
94
-
95
-
echo 'Populating default bucket with user group assignments...';
96
-
# User-group assignments go to the default bucket since they are not group-specific
97
-
nats kv put permit-groups-default metadata '{\"id\":\"permit-groups-default\",\"name\":\"Default Group\",\"description\":\"Default group for non-grouped data\",\"created_at\":\"2024-01-15T10:00:00Z\"}';
98
-
99
-
nats kv put permit-groups-default users '[\"550e8400-e29b-41d4-a716-446655440000\",\"660e8400-e29b-41d4-a716-446655440001\"]';
100
-
# nats kv put permit-groups-default users.234e5678-e89b-12d3-a456-426614174001.groups '[\"150e8400-e29b-41d4-a716-446655440000\",\"660e8400-e29b-41d4-a716-446655440001\"]';
101
-
# nats kv put permit-groups-default users.345e6789-e89b-12d3-a456-426614174002.groups '[\"160e8400-e29b-41d4-a716-446655440001\"]';
102
-
# nats kv put permit-groups-default users.456e7890-e89b-12d3-a456-426614174003.groups '[\"170e8400-e29b-41d4-a716-446655440002\"]';
103
-
# nats kv put permit-groups-default users.123e4567-e89b-12d3-a456-426614174000.groups '[\"150e8400-e29b-41d4-a716-446655440000\"]';
62
+
echo 'Setting up the single muxed NATS K/V bucket DATA (keys are <tenant>.<key>)...';
63
+
64
+
# ONE bucket holds every tenant; the group UUID is the leading key token.
65
+
nats kv add DATA --replicas=1 --history=10 --ttl=12h;
66
+
echo 'Bucket DATA created successfully';
67
+
68
+
echo 'Populating Administrators group (tenant 550e8400-e29b-41d4-a716-446655440000)...';
69
+
nats kv put DATA 550e8400-e29b-41d4-a716-446655440000.metadata '{\"id\":\"550e8400-e29b-41d4-a716-446655440000\",\"name\":\"Administrators\",\"description\":\"System administrators group\",\"created_at\":\"2024-01-15T10:00:00Z\"}';
70
+
nats kv put DATA 550e8400-e29b-41d4-a716-446655440000.members.123e4567-e89b-12d3-a456-426614174000 '{\"user_id\":\"123e4567-e89b-12d3-a456-426614174000\",\"joined_at\":\"2024-01-15T10:05:00Z\",\"role\":\"admin\"}';
71
+
nats kv put DATA 550e8400-e29b-41d4-a716-446655440000.members.234e5678-e89b-12d3-a456-426614174001 '{\"user_id\":\"234e5678-e89b-12d3-a456-426614174001\",\"joined_at\":\"2024-01-15T10:10:00Z\",\"role\":\"member\"}';
72
+
nats kv put DATA 550e8400-e29b-41d4-a716-446655440000.permissions.perm-admin-api '{\"id\":\"perm-admin-api\",\"resource\":\"api\",\"action\":\"admin\",\"effect\":\"allow\"}';
73
+
nats kv put DATA 550e8400-e29b-41d4-a716-446655440000.permissions.perm-admin-users '{\"id\":\"perm-admin-users\",\"resource\":\"users\",\"action\":\"manage\",\"effect\":\"allow\"}';
74
+
75
+
echo 'Populating Developers group (tenant 660e8400-e29b-41d4-a716-446655440001)...';
76
+
nats kv put DATA 660e8400-e29b-41d4-a716-446655440001.metadata '{\"id\":\"660e8400-e29b-41d4-a716-446655440001\",\"name\":\"Developers\",\"description\":\"Development team group\",\"created_at\":\"2024-01-15T10:30:00Z\"}';
77
+
nats kv put DATA 660e8400-e29b-41d4-a716-446655440001.members.345e6789-e89b-12d3-a456-426614174002 '{\"user_id\":\"345e6789-e89b-12d3-a456-426614174002\",\"joined_at\":\"2024-01-15T10:35:00Z\",\"role\":\"lead\"}';
78
+
nats kv put DATA 660e8400-e29b-41d4-a716-446655440001.members.234e5678-e89b-12d3-a456-426614174001 '{\"user_id\":\"234e5678-e89b-12d3-a456-426614174001\",\"joined_at\":\"2024-01-15T10:40:00Z\",\"role\":\"member\"}';
79
+
nats kv put DATA 660e8400-e29b-41d4-a716-446655440001.permissions.perm-dev-code '{\"id\":\"perm-dev-code\",\"resource\":\"code\",\"action\":\"write\",\"effect\":\"allow\"}';
80
+
nats kv put DATA 660e8400-e29b-41d4-a716-446655440001.permissions.perm-dev-deploy '{\"id\":\"perm-dev-deploy\",\"resource\":\"deployment\",\"action\":\"deploy\",\"effect\":\"allow\"}';
81
+
82
+
echo 'Populating ReadOnly Users group (tenant 770e8400-e29b-41d4-a716-446655440002)...';
83
+
nats kv put DATA 770e8400-e29b-41d4-a716-446655440002.metadata '{\"id\":\"770e8400-e29b-41d4-a716-446655440002\",\"name\":\"ReadOnly Users\",\"description\":\"Read-only access group\",\"created_at\":\"2024-01-15T11:00:00Z\"}';
84
+
nats kv put DATA 770e8400-e29b-41d4-a716-446655440002.members.456e7890-e89b-12d3-a456-426614174003 '{\"user_id\":\"456e7890-e89b-12d3-a456-426614174003\",\"joined_at\":\"2024-01-15T11:05:00Z\",\"role\":\"member\"}';
85
+
nats kv put DATA 770e8400-e29b-41d4-a716-446655440002.permissions.perm-readonly-data '{\"id\":\"perm-readonly-data\",\"resource\":\"data\",\"action\":\"read\",\"effect\":\"allow\"}';
86
+
87
+
echo 'Populating the root tenant (permit-groups-default mounts at the OPA data root)...';
88
+
nats kv put DATA permit-groups-default.metadata '{\"id\":\"permit-groups-default\",\"name\":\"Default Group\",\"description\":\"Default group for non-grouped data\",\"created_at\":\"2024-01-15T10:00:00Z\"}';
89
+
nats kv put DATA permit-groups-default.users '[\"550e8400-e29b-41d4-a716-446655440000\",\"660e8400-e29b-41d4-a716-446655440001\"]';
104
90
105
91
echo 'Test data added successfully';
106
92
echo '';
107
-
echo 'Created buckets:';
93
+
echo 'Bucket:';
108
94
nats kv list;
109
95
echo '';
110
96
echo 'Sample verification:';
111
-
echo 'Administrators group metadata (bucket 550e8400-e29b-41d4-a716-446655440000):';
112
-
nats kv get 550e8400-e29b-41d4-a716-446655440000 metadata;
113
-
echo '';
114
-
echo 'Developers group members (bucket 660e8400-e29b-41d4-a716-446655440001):';
115
-
nats kv ls 660e8400-e29b-41d4-a716-446655440001 | grep members;
97
+
echo 'Administrators group metadata (tenant 550e8400-...):';
98
+
nats kv get DATA 550e8400-e29b-41d4-a716-446655440000.metadata;
116
99
echo '';
117
-
echo 'User group assignments (default bucket):';
118
-
nats kv get permit-groups-default users.234e5678-e89b-12d3-a456-426614174001.groups;
100
+
echo 'Developers group keys (tenant 660e8400-...):';
101
+
nats kv ls DATA | grep 660e8400-e29b-41d4-a716-446655440001;
119
102
echo '';
120
-
echo 'Setup complete - ready for bucket-per-group UUID testing';
121
-
echo 'Architecture: Each group UUID is its own NATS bucket containing that groups metadata, members, and permissions';
122
-
echo 'User group assignments stored in default bucket since they span multiple groups';
103
+
echo 'Setup complete - single muxed bucket DATA, keys <tenant>.<key>';
104
+
echo 'Architecture: one bucket; each group UUID is the leading key token (the tenant);';
105
+
echo 'the plugin reads only a tenant slice via a prefix-filtered watch.';
0 commit comments