Initial import of verae-nats-accounts from zapier monorepo

This commit is contained in:
George Lambert 2026-09-11 18:13:30 -04:00
commit 358f7399c9
7 changed files with 103 additions and 0 deletions

3
NATS.md Normal file
View file

@ -0,0 +1,3 @@
# NATS — verae-nats-accounts
Not a subscriber. Defines INTERNAL vs LEAF. Leaf deny `verae.billing.>`.

8
README.md Normal file
View file

@ -0,0 +1,8 @@
# verae-nats-accounts
NATS **account + leaf allow-list**. Leaf nodes cannot publish `verae.billing.*`.
**Forgejo:** https://git.georgelambert.org/marchon/verae-nats-accounts
- `policy.json` — machine-readable allow/deny (used by access-leaf).
- `nats.conf` — sketch for a future accounts-enabled nats-server (loopback).

21
nats.conf Normal file
View file

@ -0,0 +1,21 @@
# Lab accounts sketch. Loopback only. Do not publish 4222.
# INTERNAL: middleware, account-balance, authz, identity.
# LEAF: access-leaf and remote leaf nodes — no billing.
listen: 127.0.0.1:4222
http: 127.0.0.1:8222
jetstream {}
accounts {
INTERNAL {
users = [ { user: internal, password: internal-dev } ]
jetstream: enabled
}
LEAF {
users = [ { user: leaf, password: leaf-dev } ]
exports = []
imports = []
}
}
# Subject restrictions for LEAF are enforced in policy.json + access-leaf
# until this nats-server is started with full auth callout.

10
package.json Normal file
View file

@ -0,0 +1,10 @@
{
"name": "verae-nats-accounts",
"version": "0.1.0",
"private": true,
"type": "module",
"description": "NATS account and leaf subject policy (INTERNAL vs LEAF)",
"scripts": {
"test": "node --test test/*.test.js"
}
}

35
policy.json Normal file
View file

@ -0,0 +1,35 @@
{
"accounts": {
"INTERNAL": {
"publish": [">"],
"subscribe": [">"]
},
"LEAF": {
"publish": [
"verae.access.leaf.in",
"verae.archive.put",
"verae.archive.query",
"verae.archive.reply.*",
"verae.zapier.jobs.watch",
"verae.zapier.jobs.events",
"verae.zapier.webhooks.deliver"
],
"subscribe": [
"verae.access.leaf.in",
"verae.archive.>",
"verae.zapier.jobs.>",
"verae.zapier.webhooks.deliver"
],
"deny_publish": ["verae.billing.>", "verae.access.authz.>"],
"deny_subscribe": ["verae.billing.>"]
}
},
"leafAllow": [
"verae.archive.put",
"verae.archive.query",
"verae.archive.reply.",
"verae.zapier.jobs.watch",
"verae.zapier.jobs.events",
"verae.zapier.webhooks.deliver"
]
}

14
src/policy.js Normal file
View file

@ -0,0 +1,14 @@
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const FILE = path.join(path.dirname(fileURLToPath(import.meta.url)), '..', 'policy.json');
export function loadPolicy() {
return JSON.parse(fs.readFileSync(FILE, 'utf8'));
}
export function leafAllowed(subject) {
const allow = loadPolicy().leafAllow || [];
return allow.some((p) => (p.endsWith('.') ? subject.startsWith(p) : subject === p || subject.startsWith(`${p}.`)));
}

12
test/policy.test.js Normal file
View file

@ -0,0 +1,12 @@
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { leafAllowed, loadPolicy } from '../src/policy.js';
test('leaf may archive and jobs, never billing', () => {
assert.equal(leafAllowed('verae.archive.put'), true);
assert.equal(leafAllowed('verae.archive.reply.abc'), true);
assert.equal(leafAllowed('verae.zapier.jobs.watch'), true);
assert.equal(leafAllowed('verae.billing.balance.adjust'), false);
assert.equal(leafAllowed('verae.billing.statement.get'), false);
assert.ok(loadPolicy().accounts.LEAF.deny_publish.includes('verae.billing.>'));
});