@datasynx/agentic-ai-cartography 2.4.0 → 2.6.0

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/dist/api-bin.js CHANGED
@@ -2,8 +2,8 @@
2
2
  import {
3
3
  parseApiArgs,
4
4
  startApi
5
- } from "./chunk-L4OSL7I6.js";
6
- import "./chunk-X5JA2UDT.js";
5
+ } from "./chunk-PQ7Q6MI5.js";
6
+ import "./chunk-GA4427LB.js";
7
7
  import "./chunk-QQOQBE2A.js";
8
8
  import "./chunk-2SZ5QHGH.js";
9
9
 
@@ -1774,7 +1774,10 @@ CREATE TABLE IF NOT EXISTS activity_events (
1774
1774
  duration_ms INTEGER,
1775
1775
  command TEXT,
1776
1776
  result_bytes INTEGER,
1777
- tenant TEXT NOT NULL DEFAULT 'local'
1777
+ tenant TEXT NOT NULL DEFAULT 'local',
1778
+ actor_subject TEXT,
1779
+ actor_role TEXT,
1780
+ actor_tenant TEXT
1778
1781
  );
1779
1782
 
1780
1783
  CREATE TABLE IF NOT EXISTS tasks (
@@ -1883,6 +1886,16 @@ CREATE INDEX IF NOT EXISTS idx_nodes_tenant_content ON nodes(tenant, content_has
1883
1886
  CREATE INDEX IF NOT EXISTS idx_contrib_org ON node_contributors(organization, global_id);
1884
1887
  CREATE INDEX IF NOT EXISTS idx_nodes_owner ON nodes(session_id, owner);
1885
1888
  `;
1889
+ var AUTH_SCHEMA = `
1890
+ CREATE TABLE IF NOT EXISTS auth_credentials (
1891
+ token_hash TEXT PRIMARY KEY,
1892
+ subject TEXT NOT NULL,
1893
+ tenant TEXT NOT NULL DEFAULT 'local',
1894
+ role TEXT NOT NULL,
1895
+ created_at TEXT NOT NULL
1896
+ );
1897
+ CREATE INDEX IF NOT EXISTS idx_auth_subject ON auth_credentials(subject);
1898
+ `;
1886
1899
  var CartographyDB = class {
1887
1900
  db;
1888
1901
  /** 3.6 anomaly settings; defaults apply when no `anomaly` config is supplied. */
@@ -1902,7 +1915,8 @@ var CartographyDB = class {
1902
1915
  const version = this.db.pragma("user_version", { simple: true });
1903
1916
  if (version === 0) {
1904
1917
  this.db.exec(SCHEMA);
1905
- this.db.pragma("user_version = 14");
1918
+ this.db.exec(AUTH_SCHEMA);
1919
+ this.db.pragma("user_version = 15");
1906
1920
  return;
1907
1921
  } else if (version === 1) {
1908
1922
  const cols = this.db.prepare("PRAGMA table_info(nodes)").all().map((c) => c.name);
@@ -2088,6 +2102,18 @@ var CartographyDB = class {
2088
2102
  }
2089
2103
  this.db.pragma("user_version = 14");
2090
2104
  }
2105
+ const v14 = this.db.pragma("user_version", { simple: true });
2106
+ if (v14 < 15) {
2107
+ this.db.exec(AUTH_SCHEMA);
2108
+ const ev = this.db.prepare("PRAGMA table_info(activity_events)").all();
2109
+ if (ev.length > 0) {
2110
+ const cols = ev.map((c) => c.name);
2111
+ if (!cols.includes("actor_subject")) this.db.exec("ALTER TABLE activity_events ADD COLUMN actor_subject TEXT");
2112
+ if (!cols.includes("actor_role")) this.db.exec("ALTER TABLE activity_events ADD COLUMN actor_role TEXT");
2113
+ if (!cols.includes("actor_tenant")) this.db.exec("ALTER TABLE activity_events ADD COLUMN actor_tenant TEXT");
2114
+ }
2115
+ this.db.pragma("user_version = 15");
2116
+ }
2091
2117
  }
2092
2118
  close() {
2093
2119
  this.db.pragma("optimize");
@@ -2518,13 +2544,13 @@ var CartographyDB = class {
2518
2544
  });
2519
2545
  }
2520
2546
  // ── Events ──────────────────────────────
2521
- insertEvent(sessionId, event, taskId) {
2547
+ insertEvent(sessionId, event, taskId, actor) {
2522
2548
  const id = crypto.randomUUID();
2523
2549
  const tenant = this.tenantOf(sessionId);
2524
2550
  this.db.prepare(`
2525
2551
  INSERT INTO activity_events
2526
- (id, session_id, task_id, timestamp, event_type, process, pid, target, target_type, port, command, result_bytes, tenant)
2527
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2552
+ (id, session_id, task_id, timestamp, event_type, process, pid, target, target_type, port, command, result_bytes, tenant, actor_subject, actor_role, actor_tenant)
2553
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2528
2554
  `).run(
2529
2555
  id,
2530
2556
  sessionId,
@@ -2538,9 +2564,52 @@ var CartographyDB = class {
2538
2564
  event.port ?? null,
2539
2565
  event.command ?? null,
2540
2566
  event.resultBytes ?? null,
2541
- tenant
2567
+ tenant,
2568
+ actor?.subject ?? null,
2569
+ actor?.role ?? null,
2570
+ actor?.tenant ?? null
2542
2571
  );
2543
2572
  }
2573
+ // ── RBAC credential store (4.5) ─────────────
2574
+ /** Number of stored credentials. `0` ⇒ no RBAC configured (fall back to shared/loopback). */
2575
+ countCredentials() {
2576
+ return this.db.prepare("SELECT COUNT(*) AS n FROM auth_credentials").get().n;
2577
+ }
2578
+ /** Look up a credential by its sha256 token hash. */
2579
+ findCredentialByHash(tokenHash) {
2580
+ const r = this.db.prepare("SELECT * FROM auth_credentials WHERE token_hash = ?").get(tokenHash);
2581
+ if (!r) return void 0;
2582
+ return {
2583
+ tokenHash: r["token_hash"],
2584
+ subject: r["subject"],
2585
+ tenant: r["tenant"],
2586
+ role: r["role"],
2587
+ createdAt: r["created_at"]
2588
+ };
2589
+ }
2590
+ /** Upsert a credential (idempotent on the token hash). Stores only the hash, never the raw token. */
2591
+ addCredential(rec) {
2592
+ this.db.prepare(`
2593
+ INSERT INTO auth_credentials (token_hash, subject, tenant, role, created_at)
2594
+ VALUES (?, ?, ?, ?, ?)
2595
+ ON CONFLICT(token_hash) DO UPDATE SET subject = excluded.subject, tenant = excluded.tenant, role = excluded.role
2596
+ `).run(rec.tokenHash, rec.subject, rec.tenant, rec.role, (/* @__PURE__ */ new Date()).toISOString());
2597
+ }
2598
+ /** List all credentials (token hashes only — the raw token is unrecoverable). */
2599
+ listCredentials() {
2600
+ const rows = this.db.prepare("SELECT * FROM auth_credentials ORDER BY created_at").all();
2601
+ return rows.map((r) => ({
2602
+ tokenHash: r["token_hash"],
2603
+ subject: r["subject"],
2604
+ tenant: r["tenant"],
2605
+ role: r["role"],
2606
+ createdAt: r["created_at"]
2607
+ }));
2608
+ }
2609
+ /** Revoke every credential for a subject. Returns the number removed. */
2610
+ revokeCredentialsBySubject(subject) {
2611
+ return this.db.prepare("DELETE FROM auth_credentials WHERE subject = ?").run(subject).changes;
2612
+ }
2544
2613
  getEvents(sessionId, since) {
2545
2614
  const rows = since ? this.db.prepare("SELECT * FROM activity_events WHERE session_id = ? AND timestamp > ? ORDER BY timestamp").all(sessionId, since) : this.db.prepare("SELECT * FROM activity_events WHERE session_id = ? ORDER BY timestamp").all(sessionId);
2546
2615
  return rows.map((r) => {
@@ -3217,6 +3286,9 @@ var CartographyDB = class {
3217
3286
  }
3218
3287
  };
3219
3288
 
3289
+ // src/auth/identity.ts
3290
+ import { createHash as createHash2 } from "crypto";
3291
+
3220
3292
  // src/api/auth.ts
3221
3293
  var LOOPBACK_HOSTS = /* @__PURE__ */ new Set(["127.0.0.1", "localhost", "::1", "[::1]"]);
3222
3294
  function isLoopbackHost(host) {
@@ -3237,11 +3309,6 @@ function bearerToken(header) {
3237
3309
  const token = rest.trimStart();
3238
3310
  return token.length > 0 ? token : void 0;
3239
3311
  }
3240
- function checkBearer(authorizationHeader, token) {
3241
- if (!token) return true;
3242
- const provided = bearerToken(authorizationHeader);
3243
- return provided !== void 0 && timingSafeEqual(provided, token);
3244
- }
3245
3312
  function assertSafeBind(opts) {
3246
3313
  if (isLoopbackHost(opts.host)) return;
3247
3314
  if (opts.allowedHosts === void 0) {
@@ -3259,6 +3326,54 @@ function defaultAllowedHosts(host, port) {
3259
3326
  return [`${host}:${port}`, `localhost:${port}`, `127.0.0.1:${port}`];
3260
3327
  }
3261
3328
 
3329
+ // src/auth/identity.ts
3330
+ function hashToken(token) {
3331
+ return createHash2("sha256").update(token, "utf8").digest("hex");
3332
+ }
3333
+ var SqliteCredentialStore = class {
3334
+ constructor(db) {
3335
+ this.db = db;
3336
+ }
3337
+ count() {
3338
+ return this.db.countCredentials();
3339
+ }
3340
+ findByHash(tokenHash) {
3341
+ return this.db.findCredentialByHash(tokenHash);
3342
+ }
3343
+ };
3344
+ function resolvePrincipal(presentedToken, opts) {
3345
+ const tenant = opts.defaultTenant ?? DEFAULT_TENANT;
3346
+ if (opts.store && opts.store.count() > 0) {
3347
+ if (!presentedToken) return void 0;
3348
+ const rec = opts.store.findByHash(hashToken(presentedToken));
3349
+ return rec ? { subject: rec.subject, tenant: rec.tenant, role: rec.role } : void 0;
3350
+ }
3351
+ if (opts.sharedToken) {
3352
+ if (!presentedToken || !timingSafeEqual(presentedToken, opts.sharedToken)) return void 0;
3353
+ return { subject: "shared-token", tenant, role: "admin" };
3354
+ }
3355
+ if (opts.required) return void 0;
3356
+ return { subject: "anonymous", tenant, role: "admin" };
3357
+ }
3358
+
3359
+ // src/auth/rbac.ts
3360
+ var ROLE_RANK = { viewer: 1, operator: 2, admin: 3 };
3361
+ var ACTION_MIN_ROLE = { read: "viewer", discovery: "operator", admin: "admin" };
3362
+ function can(role, action) {
3363
+ return ROLE_RANK[role] >= ROLE_RANK[ACTION_MIN_ROLE[action]];
3364
+ }
3365
+ var AuthorizationError = class extends Error {
3366
+ constructor(action, role) {
3367
+ super(`forbidden: role '${role}' may not perform '${action}'`);
3368
+ this.action = action;
3369
+ this.role = role;
3370
+ this.name = "AuthorizationError";
3371
+ }
3372
+ };
3373
+ function authorize(principal, action) {
3374
+ if (!can(principal.role, action)) throw new AuthorizationError(action, principal.role);
3375
+ }
3376
+
3262
3377
  export {
3263
3378
  sanitizeUntrusted,
3264
3379
  cloudAwsScanner,
@@ -3280,8 +3395,13 @@ export {
3280
3395
  globalId,
3281
3396
  deriveSessionName,
3282
3397
  CartographyDB,
3283
- checkBearer,
3398
+ bearerToken,
3284
3399
  assertSafeBind,
3285
- defaultAllowedHosts
3400
+ defaultAllowedHosts,
3401
+ hashToken,
3402
+ SqliteCredentialStore,
3403
+ resolvePrincipal,
3404
+ AuthorizationError,
3405
+ authorize
3286
3406
  };
3287
- //# sourceMappingURL=chunk-X5JA2UDT.js.map
3407
+ //# sourceMappingURL=chunk-GA4427LB.js.map