@sanctuary-framework/mcp-server 0.10.0 → 0.10.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/index.d.cts CHANGED
@@ -159,11 +159,29 @@ declare class AuditLog {
159
159
  private readonly maxTotalSizeBytes;
160
160
  private readonly maxEntries;
161
161
  private rotationInFlight;
162
+ private readonly pendingWrites;
162
163
  constructor(storage: StorageBackend, masterKey: Uint8Array, config?: AuditLogConfig);
163
164
  /**
164
165
  * Append an audit entry.
166
+ *
167
+ * The on-disk persist is async and tracked via `pendingWrites`. Long-lived
168
+ * callers (the main MCP server) can ignore that tracking and let writes
169
+ * drain naturally. Short-lived callers — the `sanctuary secrets` CLI which
170
+ * `process.exit()`s immediately after returning from a broker mutation —
171
+ * MUST await `flush()` before exiting, or in-flight writes get killed
172
+ * with the event loop and the entry is silently lost. That was the
173
+ * v0.10.0-rc.2 soak failure mode where `secrets audit` returned empty
174
+ * after a clean 7-verb lifecycle.
165
175
  */
166
176
  append(layer: AuditEntry["layer"], operation: string, identityId: string, details?: Record<string, unknown>, result?: "success" | "failure"): void;
177
+ /**
178
+ * Wait for every in-flight `append()` persist (and its rotation pass) to
179
+ * settle. Safe to call multiple times — newly-appended entries during a
180
+ * flush are also awaited. Re-entrant only at the granularity of "drain
181
+ * everything queued so far". Short-lived CLIs MUST call this before
182
+ * `process.exit()` to keep audit writes durable.
183
+ */
184
+ flush(): Promise<void>;
167
185
  private persistEntry;
168
186
  /**
169
187
  * Prune oldest audit entries when storage exceeds configured limits.
package/dist/index.d.ts CHANGED
@@ -159,11 +159,29 @@ declare class AuditLog {
159
159
  private readonly maxTotalSizeBytes;
160
160
  private readonly maxEntries;
161
161
  private rotationInFlight;
162
+ private readonly pendingWrites;
162
163
  constructor(storage: StorageBackend, masterKey: Uint8Array, config?: AuditLogConfig);
163
164
  /**
164
165
  * Append an audit entry.
166
+ *
167
+ * The on-disk persist is async and tracked via `pendingWrites`. Long-lived
168
+ * callers (the main MCP server) can ignore that tracking and let writes
169
+ * drain naturally. Short-lived callers — the `sanctuary secrets` CLI which
170
+ * `process.exit()`s immediately after returning from a broker mutation —
171
+ * MUST await `flush()` before exiting, or in-flight writes get killed
172
+ * with the event loop and the entry is silently lost. That was the
173
+ * v0.10.0-rc.2 soak failure mode where `secrets audit` returned empty
174
+ * after a clean 7-verb lifecycle.
165
175
  */
166
176
  append(layer: AuditEntry["layer"], operation: string, identityId: string, details?: Record<string, unknown>, result?: "success" | "failure"): void;
177
+ /**
178
+ * Wait for every in-flight `append()` persist (and its rotation pass) to
179
+ * settle. Safe to call multiple times — newly-appended entries during a
180
+ * flush are also awaited. Re-entrant only at the granularity of "drain
181
+ * everything queued so far". Short-lived CLIs MUST call this before
182
+ * `process.exit()` to keep audit writes durable.
183
+ */
184
+ flush(): Promise<void>;
167
185
  private persistEntry;
168
186
  /**
169
187
  * Prune oldest audit entries when storage exceeds configured limits.
package/dist/index.js CHANGED
@@ -1978,6 +1978,7 @@ var AuditLog = class {
1978
1978
  maxTotalSizeBytes;
1979
1979
  maxEntries;
1980
1980
  rotationInFlight = false;
1981
+ pendingWrites = /* @__PURE__ */ new Set();
1981
1982
  constructor(storage, masterKey, config) {
1982
1983
  this.storage = storage;
1983
1984
  this.encryptionKey = derivePurposeKey(masterKey, "audit-log");
@@ -1986,6 +1987,15 @@ var AuditLog = class {
1986
1987
  }
1987
1988
  /**
1988
1989
  * Append an audit entry.
1990
+ *
1991
+ * The on-disk persist is async and tracked via `pendingWrites`. Long-lived
1992
+ * callers (the main MCP server) can ignore that tracking and let writes
1993
+ * drain naturally. Short-lived callers — the `sanctuary secrets` CLI which
1994
+ * `process.exit()`s immediately after returning from a broker mutation —
1995
+ * MUST await `flush()` before exiting, or in-flight writes get killed
1996
+ * with the event loop and the entry is silently lost. That was the
1997
+ * v0.10.0-rc.2 soak failure mode where `secrets audit` returned empty
1998
+ * after a clean 7-verb lifecycle.
1989
1999
  */
1990
2000
  append(layer, operation, identityId, details, result = "success") {
1991
2001
  const entry = {
@@ -1997,8 +2007,22 @@ var AuditLog = class {
1997
2007
  details
1998
2008
  };
1999
2009
  this.entries.push(entry);
2000
- this.persistEntry(entry).catch(() => {
2010
+ const writePromise = this.persistEntry(entry).catch(() => {
2001
2011
  });
2012
+ this.pendingWrites.add(writePromise);
2013
+ void writePromise.finally(() => this.pendingWrites.delete(writePromise));
2014
+ }
2015
+ /**
2016
+ * Wait for every in-flight `append()` persist (and its rotation pass) to
2017
+ * settle. Safe to call multiple times — newly-appended entries during a
2018
+ * flush are also awaited. Re-entrant only at the granularity of "drain
2019
+ * everything queued so far". Short-lived CLIs MUST call this before
2020
+ * `process.exit()` to keep audit writes durable.
2021
+ */
2022
+ async flush() {
2023
+ while (this.pendingWrites.size > 0) {
2024
+ await Promise.allSettled([...this.pendingWrites]);
2025
+ }
2002
2026
  }
2003
2027
  async persistEntry(entry) {
2004
2028
  const key = `${Date.now()}-${this.counter++}`;
@@ -2009,7 +2033,7 @@ var AuditLog = class {
2009
2033
  key,
2010
2034
  stringToBytes(JSON.stringify(encrypted))
2011
2035
  );
2012
- this.maybeRotate().catch(() => {
2036
+ await this.maybeRotate().catch(() => {
2013
2037
  });
2014
2038
  }
2015
2039
  /**