edge-book 0.15.0 → 0.15.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/dist/edge-book.js +97 -14
- package/package.json +1 -1
package/dist/edge-book.js
CHANGED
|
@@ -935,6 +935,7 @@ async function canReadObject(store, objectId, subjectAgentId, at = Date.now()) {
|
|
|
935
935
|
for (const grant of candidates) {
|
|
936
936
|
if (await store.verifyGrantSignature(grant)) return true;
|
|
937
937
|
}
|
|
938
|
+
await store.audit("object.read.denied", subjectAgentId, { object_id: objectId, scope: "object.read" });
|
|
938
939
|
return false;
|
|
939
940
|
}
|
|
940
941
|
async function readObject(store, objectId, subjectAgentId) {
|
|
@@ -974,6 +975,7 @@ async function shareObjectEnvelope(store, peerAgentId, objectId, expiresAt = "")
|
|
|
974
975
|
if (object.attachment) {
|
|
975
976
|
attachment_b64 = (await fs5.readFile(store.file(object.attachment.ref))).toString("base64");
|
|
976
977
|
}
|
|
978
|
+
await store.audit("object.share", peerAgentId, { object_id: objectId, grant_id: grant.grant_id, scope: "object.read" });
|
|
977
979
|
return store.signEnvelope({
|
|
978
980
|
type: "object_share",
|
|
979
981
|
to_agent_id: peerAgentId,
|
|
@@ -1019,7 +1021,7 @@ async function revokeObjectGrant(store, objectId, subjectAgentId) {
|
|
|
1019
1021
|
}
|
|
1020
1022
|
if (revoked.length) {
|
|
1021
1023
|
await store.saveGrants(grants);
|
|
1022
|
-
await store.audit("grant.revoke", subjectAgentId, { object_id: objectId, grant_ids: revoked });
|
|
1024
|
+
await store.audit("grant.revoke", subjectAgentId, { object_id: objectId, grant_ids: revoked, scope: "object.read" });
|
|
1023
1025
|
}
|
|
1024
1026
|
return revoked;
|
|
1025
1027
|
}
|
|
@@ -1823,13 +1825,18 @@ async function broadcastProfileEnvelopes(store) {
|
|
|
1823
1825
|
async function revoke(store, peerAgentId) {
|
|
1824
1826
|
await store.setRelationship(peerAgentId, "revoked", "Revoke", "revoked");
|
|
1825
1827
|
const grants = await store.grants();
|
|
1828
|
+
const revoked = [];
|
|
1829
|
+
const scopes = /* @__PURE__ */ new Set();
|
|
1826
1830
|
for (const grant of Object.values(grants)) {
|
|
1827
1831
|
if (grant.subject_agent_id === peerAgentId || grant.issuer_agent_id === peerAgentId) {
|
|
1828
1832
|
grant.status = "revoked";
|
|
1829
1833
|
grant.revoked_at = now2();
|
|
1834
|
+
revoked.push(grant.grant_id);
|
|
1835
|
+
for (const scope of grant.scopes) scopes.add(scope);
|
|
1830
1836
|
}
|
|
1831
1837
|
}
|
|
1832
1838
|
await store.saveGrants(grants);
|
|
1839
|
+
if (revoked.length) await store.audit("grant.revoke", peerAgentId, { grant_ids: revoked, scopes: [...scopes].sort() });
|
|
1833
1840
|
}
|
|
1834
1841
|
async function block(store, peerAgentId) {
|
|
1835
1842
|
await store.setRelationship(peerAgentId, "blocked", "Block", "blocked");
|
|
@@ -2505,15 +2512,36 @@ async function receiveEnvelope(store, envelope) {
|
|
|
2505
2512
|
}
|
|
2506
2513
|
async function audit(store, action, peerAgentId, details) {
|
|
2507
2514
|
const audit_id = randomId("audit");
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
+
try {
|
|
2516
|
+
const identity = await store.identity();
|
|
2517
|
+
await appendJsonl(store.file(AUDIT_FILE), {
|
|
2518
|
+
audit_id,
|
|
2519
|
+
created_at: now2(),
|
|
2520
|
+
kind: action,
|
|
2521
|
+
action,
|
|
2522
|
+
actor_agent_id: identity.agent_id,
|
|
2523
|
+
peer_agent_id: peerAgentId,
|
|
2524
|
+
...auditIndexFields(details),
|
|
2525
|
+
details
|
|
2526
|
+
});
|
|
2527
|
+
} catch {
|
|
2528
|
+
}
|
|
2515
2529
|
return audit_id;
|
|
2516
2530
|
}
|
|
2531
|
+
function auditIndexFields(details) {
|
|
2532
|
+
const fields = {};
|
|
2533
|
+
const objectId = details.object_id;
|
|
2534
|
+
const grantId = details.grant_id;
|
|
2535
|
+
const grantIds = details.grant_ids;
|
|
2536
|
+
const scope = details.scope;
|
|
2537
|
+
const scopes = details.scopes;
|
|
2538
|
+
if (typeof objectId === "string") fields.object_id = objectId;
|
|
2539
|
+
if (typeof grantId === "string") fields.grant_id = grantId;
|
|
2540
|
+
if (Array.isArray(grantIds) && grantIds.every((id) => typeof id === "string")) fields.grant_ids = grantIds.join(",");
|
|
2541
|
+
if (typeof scope === "string") fields.grant_scope = scope;
|
|
2542
|
+
if (Array.isArray(scopes) && scopes.every((s) => typeof s === "string")) fields.grant_scope = scopes.join(",");
|
|
2543
|
+
return fields;
|
|
2544
|
+
}
|
|
2517
2545
|
|
|
2518
2546
|
// src/store-notify.ts
|
|
2519
2547
|
async function peerName(store, agentId) {
|
|
@@ -5819,6 +5847,7 @@ function ensureGreeterCron(opts) {
|
|
|
5819
5847
|
// src/doctor.ts
|
|
5820
5848
|
var DOCTOR_EVENT_TAIL = 50;
|
|
5821
5849
|
var DOCTOR_TRACE_TAIL = 10;
|
|
5850
|
+
var DOCTOR_AUDIT_TAIL = 20;
|
|
5822
5851
|
var DEFAULT_RELAY_TIMEOUT_MS = 3e3;
|
|
5823
5852
|
function recentTraces(events, limit = DOCTOR_TRACE_TAIL) {
|
|
5824
5853
|
const out = [];
|
|
@@ -5857,6 +5886,42 @@ function notifierCronState(runner) {
|
|
|
5857
5886
|
return "error";
|
|
5858
5887
|
}
|
|
5859
5888
|
}
|
|
5889
|
+
function stringField(value) {
|
|
5890
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
5891
|
+
}
|
|
5892
|
+
function detailString(details, key) {
|
|
5893
|
+
if (!details || typeof details !== "object") return void 0;
|
|
5894
|
+
const value = details[key];
|
|
5895
|
+
if (typeof value === "string" && value.length > 0) return value;
|
|
5896
|
+
if (Array.isArray(value) && value.every((entry) => typeof entry === "string")) return value.join(",");
|
|
5897
|
+
return void 0;
|
|
5898
|
+
}
|
|
5899
|
+
function firstString(...values) {
|
|
5900
|
+
for (const value of values) {
|
|
5901
|
+
const found = stringField(value);
|
|
5902
|
+
if (found) return found;
|
|
5903
|
+
}
|
|
5904
|
+
return void 0;
|
|
5905
|
+
}
|
|
5906
|
+
function addAuditField(event, key, value) {
|
|
5907
|
+
if (value) event[key] = value;
|
|
5908
|
+
}
|
|
5909
|
+
function auditEvent(event) {
|
|
5910
|
+
const out = {
|
|
5911
|
+
ts: firstString(event.created_at, event.ts) ?? "",
|
|
5912
|
+
kind: firstString(event.kind, event.action) ?? "unknown"
|
|
5913
|
+
};
|
|
5914
|
+
addAuditField(out, "actor_agent_id", stringField(event.actor_agent_id));
|
|
5915
|
+
addAuditField(out, "peer_agent_id", stringField(event.peer_agent_id));
|
|
5916
|
+
addAuditField(out, "object_id", firstString(event.object_id, detailString(event.details, "object_id")));
|
|
5917
|
+
addAuditField(out, "grant_id", firstString(event.grant_id, detailString(event.details, "grant_id")));
|
|
5918
|
+
addAuditField(out, "grant_ids", firstString(event.grant_ids, detailString(event.details, "grant_ids")));
|
|
5919
|
+
addAuditField(out, "grant_scope", firstString(event.grant_scope, detailString(event.details, "scope"), detailString(event.details, "scopes")));
|
|
5920
|
+
return out;
|
|
5921
|
+
}
|
|
5922
|
+
function auditTail(events, limit = DOCTOR_AUDIT_TAIL) {
|
|
5923
|
+
return events.slice(-limit).map(auditEvent);
|
|
5924
|
+
}
|
|
5860
5925
|
async function buildDoctorReport(store, opts) {
|
|
5861
5926
|
const legacy = await store.doctor();
|
|
5862
5927
|
const config = await store.config();
|
|
@@ -5917,6 +5982,7 @@ async function buildDoctorReport(store, opts) {
|
|
|
5917
5982
|
},
|
|
5918
5983
|
stores,
|
|
5919
5984
|
events: await readEvents(store, DOCTOR_EVENT_TAIL),
|
|
5985
|
+
audit: auditTail(await store.auditEvents()),
|
|
5920
5986
|
// Traces are derived from the FULL event log (not just the tail) so a
|
|
5921
5987
|
// busy log does not hide an older still-relevant trace.
|
|
5922
5988
|
traces: recentTraces(await readEvents(store))
|
|
@@ -5926,6 +5992,25 @@ function renderEventLine(e) {
|
|
|
5926
5992
|
const extras = Object.entries(e).filter(([k]) => k !== "ts" && k !== "kind").map(([k, v]) => `${k}=${String(v)}`).join(" ");
|
|
5927
5993
|
return ` ${e.ts} ${e.kind}${extras ? ` ${extras}` : ""}`;
|
|
5928
5994
|
}
|
|
5995
|
+
function renderAuditLine(e) {
|
|
5996
|
+
const extras = Object.entries(e).filter(([k]) => k !== "ts" && k !== "kind").map(([k, v]) => `${k}=${v}`).join(" ");
|
|
5997
|
+
return ` ${e.ts} ${e.kind}${extras ? ` ${extras}` : ""}`;
|
|
5998
|
+
}
|
|
5999
|
+
function appendTraceSection(lines, traces) {
|
|
6000
|
+
lines.push(`Recent traces (distinct trace_ids, newest last, up to ${DOCTOR_TRACE_TAIL})`);
|
|
6001
|
+
if (traces.length === 0) lines.push(" (no traced envelopes yet)");
|
|
6002
|
+
for (const t of traces) lines.push(` ${t.ts} ${t.direction === "out" ? "\u2192" : "\u2190"} ${t.kind} ${t.trace_id}`);
|
|
6003
|
+
}
|
|
6004
|
+
function appendEventSection(lines, events) {
|
|
6005
|
+
lines.push(`Event log (newest last, up to ${DOCTOR_EVENT_TAIL})`);
|
|
6006
|
+
if (events.length === 0) lines.push(" (no events recorded yet)");
|
|
6007
|
+
for (const e of events) lines.push(renderEventLine(e));
|
|
6008
|
+
}
|
|
6009
|
+
function appendAuditSection(lines, audit2) {
|
|
6010
|
+
lines.push(`Audit log (newest last, up to ${DOCTOR_AUDIT_TAIL})`);
|
|
6011
|
+
if (audit2.length === 0) lines.push(" (no audit records yet)");
|
|
6012
|
+
for (const e of audit2) lines.push(renderAuditLine(e));
|
|
6013
|
+
}
|
|
5929
6014
|
function renderDoctorText(report) {
|
|
5930
6015
|
const lines = [];
|
|
5931
6016
|
lines.push(`Edge Book doctor \u2014 v${report.version} (${report.generated_at})`);
|
|
@@ -5963,13 +6048,11 @@ function renderDoctorText(report) {
|
|
|
5963
6048
|
lines.push(` contacts: ${report.stores.contacts} (friends: ${report.stores.friends})`);
|
|
5964
6049
|
lines.push(` posts: ${report.stores.posts} objects: ${report.stores.objects} escalations: ${report.stores.escalations} pending approvals: ${report.stores.pending_approvals}`);
|
|
5965
6050
|
lines.push("");
|
|
5966
|
-
lines.
|
|
5967
|
-
if (report.traces.length === 0) lines.push(" (no traced envelopes yet)");
|
|
5968
|
-
for (const t of report.traces) lines.push(` ${t.ts} ${t.direction === "out" ? "\u2192" : "\u2190"} ${t.kind} ${t.trace_id}`);
|
|
6051
|
+
appendTraceSection(lines, report.traces);
|
|
5969
6052
|
lines.push("");
|
|
5970
|
-
lines
|
|
5971
|
-
|
|
5972
|
-
|
|
6053
|
+
appendEventSection(lines, report.events);
|
|
6054
|
+
lines.push("");
|
|
6055
|
+
appendAuditSection(lines, report.audit);
|
|
5973
6056
|
return lines.join("\n");
|
|
5974
6057
|
}
|
|
5975
6058
|
|