@shieldaai/agent-map 0.1.0-alpha.2 → 1.0.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.
Files changed (96) hide show
  1. package/README.md +653 -268
  2. package/dist/assets.js +16 -26
  3. package/dist/boundary-flags.d.ts +73 -0
  4. package/dist/boundary-flags.js +352 -0
  5. package/dist/boundary.d.ts +19 -2
  6. package/dist/boundary.js +75 -19
  7. package/dist/cli-entry.d.ts +13 -0
  8. package/dist/cli-entry.js +66 -0
  9. package/dist/cli.js +41 -44
  10. package/dist/common.d.ts +34 -0
  11. package/dist/common.js +94 -0
  12. package/dist/http.js +78 -51
  13. package/dist/identity-cli.js +94 -47
  14. package/dist/identity-http.js +226 -94
  15. package/dist/index.d.ts +4 -4
  16. package/dist/index.js +4 -4
  17. package/dist/mcp.js +40 -5
  18. package/dist/oidc.d.ts +5 -0
  19. package/dist/oidc.js +121 -38
  20. package/dist/policy-reader.d.ts +56 -3
  21. package/dist/policy-reader.js +823 -110
  22. package/dist/projection-fields.d.ts +4 -0
  23. package/dist/projection-fields.js +144 -0
  24. package/dist/service/changes.d.ts +5 -0
  25. package/dist/service/changes.js +255 -0
  26. package/dist/service/context.d.ts +33 -0
  27. package/dist/service/context.js +2 -0
  28. package/dist/service/discovery.d.ts +6 -0
  29. package/dist/service/discovery.js +31 -0
  30. package/dist/service/explain.d.ts +12 -0
  31. package/dist/service/explain.js +126 -0
  32. package/dist/service/flags.d.ts +20 -0
  33. package/dist/service/flags.js +70 -0
  34. package/dist/service/inventory.d.ts +4 -0
  35. package/dist/service/inventory.js +62 -0
  36. package/dist/service/json.d.ts +8 -0
  37. package/dist/service/json.js +7 -0
  38. package/dist/service/operations/discovery.d.ts +5 -0
  39. package/dist/service/operations/discovery.js +90 -0
  40. package/dist/service/operations/explain-access.d.ts +3 -0
  41. package/dist/service/operations/explain-access.js +25 -0
  42. package/dist/service/operations/find-agents-with-access.d.ts +3 -0
  43. package/dist/service/operations/find-agents-with-access.js +120 -0
  44. package/dist/service/operations/get-agent-access.d.ts +3 -0
  45. package/dist/service/operations/get-agent-access.js +75 -0
  46. package/dist/service/operations/get-agent-inventory.d.ts +3 -0
  47. package/dist/service/operations/get-agent-inventory.js +42 -0
  48. package/dist/service/operations/get-changes.d.ts +3 -0
  49. package/dist/service/operations/get-changes.js +11 -0
  50. package/dist/service/operations/get-inventory-health.d.ts +3 -0
  51. package/dist/service/operations/get-inventory-health.js +81 -0
  52. package/dist/service/operations/get-resource-access.d.ts +3 -0
  53. package/dist/service/operations/get-resource-access.js +48 -0
  54. package/dist/service/operations/list-agents.d.ts +3 -0
  55. package/dist/service/operations/list-agents.js +92 -0
  56. package/dist/service/operations/list-owners-resources.d.ts +5 -0
  57. package/dist/service/operations/list-owners-resources.js +69 -0
  58. package/dist/service/ordering.d.ts +9 -0
  59. package/dist/service/ordering.js +47 -0
  60. package/dist/service/outside-guard.d.ts +68 -0
  61. package/dist/service/outside-guard.js +313 -0
  62. package/dist/service/policy.d.ts +26 -0
  63. package/dist/service/policy.js +299 -0
  64. package/dist/service/schemas.d.ts +16 -0
  65. package/dist/service/schemas.js +192 -0
  66. package/dist/service/summaries.d.ts +32 -0
  67. package/dist/service/summaries.js +247 -0
  68. package/dist/service/work.d.ts +33 -0
  69. package/dist/service/work.js +116 -0
  70. package/dist/service.d.ts +5 -5
  71. package/dist/service.js +110 -645
  72. package/dist/store/ancestors.d.ts +9 -0
  73. package/dist/store/ancestors.js +86 -0
  74. package/dist/store/discovery.d.ts +8 -0
  75. package/dist/store/discovery.js +288 -0
  76. package/dist/store/evidence.d.ts +12 -0
  77. package/dist/store/evidence.js +90 -0
  78. package/dist/store/identity-scope.d.ts +9 -0
  79. package/dist/store/identity-scope.js +71 -0
  80. package/dist/store/nodes.d.ts +24 -0
  81. package/dist/store/nodes.js +237 -0
  82. package/dist/store/projection.d.ts +19 -0
  83. package/dist/store/projection.js +242 -0
  84. package/dist/store/queries.d.ts +48 -0
  85. package/dist/store/queries.js +302 -0
  86. package/dist/store/read.d.ts +81 -0
  87. package/dist/store/read.js +135 -0
  88. package/dist/store/selection.d.ts +3 -0
  89. package/dist/store/selection.js +102 -0
  90. package/dist/store.d.ts +15 -0
  91. package/dist/store.js +102 -395
  92. package/dist/transport.d.ts +42 -2
  93. package/dist/transport.js +144 -16
  94. package/dist/types.d.ts +184 -4
  95. package/dist/types.js +52 -11
  96. package/package.json +32 -5
package/dist/oidc.js CHANGED
@@ -2,9 +2,10 @@ import { request as httpsRequest } from 'node:https';
2
2
  import { lookup } from 'node:dns';
3
3
  import { X509Certificate } from 'node:crypto';
4
4
  import { rootCertificates, createSecureContext } from 'node:tls';
5
- import { immutableJson } from '@shieldaai/agent-guard';
5
+ import { immutableJson } from '@shieldaai/agent-guard/internal';
6
6
  import * as oidc from 'openid-client';
7
- import { fields, integer, fail, parseRequest } from './boundary.js';
7
+ import { integer, fail } from './common.js';
8
+ import { fields, parseRequest } from './boundary.js';
8
9
  import { MapQueryError, mapIdentifier } from './types.js';
9
10
  const INVALID = 'MAP_IDENTITY_INVALID', UNAVAILABLE = 'MAP_IDENTITY_UNAVAILABLE';
10
11
  function httpsUrl(value, code, query = false) {
@@ -35,8 +36,18 @@ export async function createMapOidcClient(options) {
35
36
  catch {
36
37
  return fail('MAP_CONFIG_INVALID');
37
38
  }
38
- const config = fields(copied, ['issuer', 'clientId', 'clientSecret', 'redirectUri', 'clientAuthMethod',
39
- 'endpointOrigins', 'ca', 'requestTimeoutMs', 'maxResponseBytes'], ['issuer', 'clientId', 'clientSecret', 'redirectUri']);
39
+ const config = fields(copied, [
40
+ 'issuer',
41
+ 'clientId',
42
+ 'clientSecret',
43
+ 'redirectUri',
44
+ 'clientAuthMethod',
45
+ 'endpointOrigins',
46
+ 'ca',
47
+ 'requestTimeoutMs',
48
+ 'maxResponseBytes',
49
+ 'clockToleranceSeconds',
50
+ ], ['issuer', 'clientId', 'clientSecret', 'redirectUri']);
40
51
  const issuer = httpsUrl(config.issuer, 'MAP_CONFIG_INVALID');
41
52
  if (issuer.pathname.includes('/.well-known/'))
42
53
  fail('MAP_CONFIG_INVALID');
@@ -53,6 +64,9 @@ export async function createMapOidcClient(options) {
53
64
  const authentication = () => method === 'client_secret_basic' ? oidc.ClientSecretBasic(secret) : oidc.ClientSecretPost(secret);
54
65
  const timeout = integer(config.requestTimeoutMs, 5000, 50, 10_000);
55
66
  const maxBytes = integer(config.maxResponseBytes, 256 * 1024, 1024, 1024 * 1024);
67
+ // An identity provider's clock is rarely the same second as ours. The tolerance lets a token
68
+ // issued (or valid from) slightly in our future through; expiry is checked below without it.
69
+ const clockTolerance = integer(config.clockToleranceSeconds, 5, 0, 60);
56
70
  const origins = new Set([issuer.origin]);
57
71
  if (config.endpointOrigins !== undefined) {
58
72
  if (!Array.isArray(config.endpointOrigins) || config.endpointOrigins.length > 8)
@@ -69,7 +83,9 @@ export async function createMapOidcClient(options) {
69
83
  if (typeof config.ca !== 'string' || !config.ca || Buffer.byteLength(config.ca) > 256 * 1024)
70
84
  fail('MAP_CONFIG_INVALID');
71
85
  const certificates = config.ca.match(/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/g);
72
- if (!certificates?.length || certificates.length > 16 || config.ca.replace(/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/g, '').trim())
86
+ if (!certificates?.length ||
87
+ certificates.length > 16 ||
88
+ config.ca.replace(/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/g, '').trim())
73
89
  fail('MAP_CONFIG_INVALID');
74
90
  try {
75
91
  for (const certificate of certificates)
@@ -90,8 +106,10 @@ export async function createMapOidcClient(options) {
90
106
  function operation(external) {
91
107
  const deadline = performance.now() + timeout, active = new Set();
92
108
  let closed = false, failure;
93
- const check = () => { if (closed || external?.aborted || performance.now() >= deadline)
94
- fail(UNAVAILABLE); };
109
+ const check = () => {
110
+ if (closed || external?.aborted || performance.now() >= deadline)
111
+ fail(UNAVAILABLE);
112
+ };
95
113
  const fetch = (input, init) => new Promise((resolve, reject) => {
96
114
  let request, settled = false, counted = false;
97
115
  let timer;
@@ -154,17 +172,30 @@ export async function createMapOidcClient(options) {
154
172
  callback(error, addresses?.[0]?.address ?? '', addresses?.[0]?.family);
155
173
  });
156
174
  };
157
- request = httpsRequest(url, { method: init.method, headers: { ...init.headers, 'accept-encoding': 'identity', connection: 'close' },
158
- agent: false, rejectUnauthorized: true, minVersion: 'TLSv1.2', maxHeaderSize: 8192,
159
- lookup: boundedLookup, ...(ca === undefined ? {} : { ca }) }, response => {
175
+ request = httpsRequest(url, {
176
+ method: init.method,
177
+ headers: { ...init.headers, 'accept-encoding': 'identity', connection: 'close' },
178
+ agent: false,
179
+ rejectUnauthorized: true,
180
+ minVersion: 'TLSv1.2',
181
+ maxHeaderSize: 8192,
182
+ lookup: boundedLookup,
183
+ ...(ca === undefined ? {} : { ca }),
184
+ }, response => {
160
185
  const status = response.statusCode ?? 0;
161
186
  const count = (name) => response.rawHeaders.filter((item, index) => index % 2 === 0 && item.toLowerCase() === name).length;
162
187
  const length = response.headers['content-length'];
163
- if ((status >= 300 && status < 400) || status >= 500 || status < 200 ||
164
- count('content-length') > 1 || count('content-type') !== 1 || count('transfer-encoding') > 1 ||
165
- response.headers['content-encoding'] !== undefined || (length !== undefined && response.headers['transfer-encoding'] !== undefined) ||
188
+ if ((status >= 300 && status < 400) ||
189
+ status >= 500 ||
190
+ status < 200 ||
191
+ count('content-length') > 1 ||
192
+ count('content-type') !== 1 ||
193
+ count('transfer-encoding') > 1 ||
194
+ response.headers['content-encoding'] !== undefined ||
195
+ (length !== undefined && response.headers['transfer-encoding'] !== undefined) ||
166
196
  !/^application\/(?:json|jwk-set\+json)(?:\s*;\s*charset=utf-8)?$/i.test(response.headers['content-type'] ?? '') ||
167
- (length !== undefined && (!/^\d+$/.test(length) || !Number.isSafeInteger(Number(length)) || Number(length) > maxBytes))) {
197
+ (length !== undefined &&
198
+ (!/^\d+$/.test(length) || !Number.isSafeInteger(Number(length)) || Number(length) > maxBytes))) {
168
199
  response.destroy();
169
200
  stop(UNAVAILABLE);
170
201
  return;
@@ -221,30 +252,53 @@ export async function createMapOidcClient(options) {
221
252
  stop(error instanceof MapQueryError ? error.code : UNAVAILABLE);
222
253
  }
223
254
  });
224
- return { fetch, check, get failure() { return failure; }, close() { closed = true; for (const request of active)
225
- request.destroy(); } };
255
+ return {
256
+ fetch,
257
+ check,
258
+ get failure() {
259
+ return failure;
260
+ },
261
+ close() {
262
+ closed = true;
263
+ for (const request of active)
264
+ request.destroy();
265
+ },
266
+ };
226
267
  }
227
- const clientMetadata = { id_token_signed_response_alg: 'RS256', [oidc.clockTolerance]: 0 };
268
+ const clientMetadata = { id_token_signed_response_alg: 'RS256', [oidc.clockTolerance]: clockTolerance };
228
269
  const discovery = operation();
229
270
  let metadata;
230
271
  try {
231
- const discovered = await oidc.discovery(issuer, clientId, clientMetadata, authentication(), { [oidc.customFetch]: discovery.fetch, timeout: timeout / 1000 });
272
+ const discovered = await oidc.discovery(issuer, clientId, clientMetadata, authentication(), {
273
+ [oidc.customFetch]: discovery.fetch,
274
+ timeout: timeout / 1000,
275
+ });
232
276
  discovery.check();
233
277
  const remote = discovered.serverMetadata();
234
- if (remote.issuer !== config.issuer || !Array.isArray(remote.id_token_signing_alg_values_supported) ||
278
+ if (remote.issuer !== config.issuer ||
279
+ !Array.isArray(remote.id_token_signing_alg_values_supported) ||
235
280
  !remote.id_token_signing_alg_values_supported.includes('RS256') ||
236
- !Array.isArray(remote.code_challenge_methods_supported) || !remote.code_challenge_methods_supported.includes('S256') ||
237
- (remote.token_endpoint_auth_methods_supported === undefined ? method !== 'client_secret_basic' :
238
- !Array.isArray(remote.token_endpoint_auth_methods_supported) || !remote.token_endpoint_auth_methods_supported.includes(method)))
281
+ !Array.isArray(remote.code_challenge_methods_supported) ||
282
+ !remote.code_challenge_methods_supported.includes('S256') ||
283
+ (remote.token_endpoint_auth_methods_supported === undefined
284
+ ? method !== 'client_secret_basic'
285
+ : !Array.isArray(remote.token_endpoint_auth_methods_supported) ||
286
+ !remote.token_endpoint_auth_methods_supported.includes(method)))
239
287
  fail(INVALID);
240
288
  for (const name of ['authorization_endpoint', 'token_endpoint', 'jwks_uri']) {
241
289
  if (!origins.has(httpsUrl(remote[name], INVALID).origin))
242
290
  fail(INVALID);
243
291
  }
244
- metadata = { issuer: remote.issuer, authorization_endpoint: remote.authorization_endpoint,
245
- token_endpoint: remote.token_endpoint, jwks_uri: remote.jwks_uri, id_token_signing_alg_values_supported: ['RS256'],
246
- code_challenge_methods_supported: ['S256'], token_endpoint_auth_methods_supported: [method],
247
- authorization_response_iss_parameter_supported: remote.authorization_response_iss_parameter_supported === true };
292
+ metadata = {
293
+ issuer: remote.issuer,
294
+ authorization_endpoint: remote.authorization_endpoint,
295
+ token_endpoint: remote.token_endpoint,
296
+ jwks_uri: remote.jwks_uri,
297
+ id_token_signing_alg_values_supported: ['RS256'],
298
+ code_challenge_methods_supported: ['S256'],
299
+ token_endpoint_auth_methods_supported: [method],
300
+ authorization_response_iss_parameter_supported: remote.authorization_response_iss_parameter_supported === true,
301
+ };
248
302
  }
249
303
  catch (error) {
250
304
  throw discovery.failure ?? new MapQueryError(error instanceof MapQueryError ? error.code : INVALID);
@@ -264,8 +318,15 @@ export async function createMapOidcClient(options) {
264
318
  const op = operation();
265
319
  try {
266
320
  const state = oidc.randomState(), nonce = oidc.randomNonce(), codeVerifier = oidc.randomPKCECodeVerifier();
267
- const authorizationUrl = oidc.buildAuthorizationUrl(configuration(op.fetch), { response_type: 'code', scope: 'openid',
268
- redirect_uri: redirect.href, state, nonce, code_challenge_method: 'S256', code_challenge: await oidc.calculatePKCECodeChallenge(codeVerifier) });
321
+ const authorizationUrl = oidc.buildAuthorizationUrl(configuration(op.fetch), {
322
+ response_type: 'code',
323
+ scope: 'openid',
324
+ redirect_uri: redirect.href,
325
+ state,
326
+ nonce,
327
+ code_challenge_method: 'S256',
328
+ code_challenge: await oidc.calculatePKCECodeChallenge(codeVerifier),
329
+ });
269
330
  op.check();
270
331
  return Object.freeze({ authorizationUrl: authorizationUrl.href, state, nonce, codeVerifier });
271
332
  }
@@ -280,28 +341,50 @@ export async function createMapOidcClient(options) {
280
341
  const op = operation(signal);
281
342
  try {
282
343
  op.check();
283
- if (!(callbackUrl instanceof URL) || callbackUrl.href.length > 8192 || callbackUrl.origin !== redirect.origin ||
284
- callbackUrl.pathname !== redirect.pathname || callbackUrl.hash || callbackUrl.username || callbackUrl.password)
344
+ if (!(callbackUrl instanceof URL) ||
345
+ callbackUrl.href.length > 8192 ||
346
+ callbackUrl.origin !== redirect.origin ||
347
+ callbackUrl.pathname !== redirect.pathname ||
348
+ callbackUrl.hash ||
349
+ callbackUrl.username ||
350
+ callbackUrl.password)
285
351
  fail(INVALID);
286
352
  const callback = new URL(callbackUrl.href), names = [...callback.searchParams.keys()];
287
- if (names.length > 16 || new Set(names).size !== names.length || names.some(name => !name || name.length > 128) ||
288
- callback.searchParams.get('error') !== null || callback.searchParams.get('response') !== null ||
289
- !callback.searchParams.get('code') || !callback.searchParams.get('state'))
353
+ if (names.length > 16 ||
354
+ new Set(names).size !== names.length ||
355
+ names.some(name => !name || name.length > 128) ||
356
+ callback.searchParams.get('error') !== null ||
357
+ callback.searchParams.get('response') !== null ||
358
+ !callback.searchParams.get('code') ||
359
+ !callback.searchParams.get('state'))
290
360
  fail(INVALID);
291
361
  const value = fields(transaction, ['state', 'nonce', 'codeVerifier'], ['state', 'nonce', 'codeVerifier']);
292
362
  const state = opaque(value.state), nonce = opaque(value.nonce), verifier = opaque(value.codeVerifier);
293
- const tokens = await oidc.authorizationCodeGrant(configuration(op.fetch), callback, { expectedState: state, expectedNonce: nonce, pkceCodeVerifier: verifier, idTokenExpected: true });
363
+ const tokens = await oidc.authorizationCodeGrant(configuration(op.fetch), callback, {
364
+ expectedState: state,
365
+ expectedNonce: nonce,
366
+ pkceCodeVerifier: verifier,
367
+ idTokenExpected: true,
368
+ });
294
369
  op.check();
295
370
  const claims = tokens.claims(), now = Date.now();
296
- if (!claims || claims.iss !== config.issuer || (claims.azp !== undefined && claims.azp !== clientId) ||
297
- !Number.isSafeInteger(claims.exp) || !Number.isSafeInteger(claims.iat) || claims.iat > Math.floor(now / 1000) ||
298
- claims.exp <= claims.iat || !Number.isSafeInteger(claims.exp * 1000) || claims.exp * 1000 <= now)
371
+ if (!claims ||
372
+ claims.iss !== config.issuer ||
373
+ (claims.azp !== undefined && claims.azp !== clientId) ||
374
+ !Number.isSafeInteger(claims.exp) ||
375
+ !Number.isSafeInteger(claims.iat) ||
376
+ claims.iat > Math.floor(now / 1000) + clockTolerance ||
377
+ claims.exp <= claims.iat ||
378
+ !Number.isSafeInteger(claims.exp * 1000) ||
379
+ // The protocol library's tolerance also covers `exp`; this check does not.
380
+ claims.exp * 1000 <= now)
299
381
  fail(INVALID);
300
382
  const subject = mapIdentifier(claims.sub, INVALID);
301
383
  return Object.freeze({ subject, expiresAt: claims.exp * 1000 });
302
384
  }
303
385
  catch (error) {
304
- throw op.failure ?? new MapQueryError(error instanceof MapQueryError && error.code === UNAVAILABLE ? UNAVAILABLE : INVALID);
386
+ throw (op.failure ??
387
+ new MapQueryError(error instanceof MapQueryError && error.code === UNAVAILABLE ? UNAVAILABLE : INVALID));
305
388
  }
306
389
  finally {
307
390
  op.close();
@@ -1,4 +1,4 @@
1
- import { type JsonObject, type MapEdge, type MapNode, type MapPolicyCandidate, type MapPolicyEvidence } from './types.js';
1
+ import { type JsonObject, type MapEdge, type MapNode, type MapPolicyCandidate, type MapPolicyEvidence, type MapPolicySelection, type MapIdentityScope } from './types.js';
2
2
  interface Probe {
3
3
  readonly key: number;
4
4
  readonly identity: MapNode;
@@ -17,12 +17,65 @@ export interface PolicyPlan {
17
17
  readonly records: readonly MapPolicyEvidence[];
18
18
  readonly probes: readonly Probe[];
19
19
  }
20
- /** Pair only the selected task's grants with its deployment's declared execution identity. */
21
- export declare function policyPlan(companyId: string, tenantId: string, nodes: readonly MapNode[], edges: readonly MapEdge[], maximum: number): PolicyPlan;
20
+ /** Python's scope ID uses compact ensure_ascii JSON; preserve that byte contract. */
21
+ export declare function collectionKey(companyId: string, tenantId: string, partition: string, accountId: string): string;
22
+ /**
23
+ * Pair only the selected task's grants with its deployment's declared execution identity.
24
+ * `select` narrows the pairs to one task, tool, resource reference (or mapped ARN) or action,
25
+ * so a read that shows a few pairs is not bound by the size of the whole fleet.
26
+ */
27
+ export declare function policyPlan(companyId: string, tenantId: string, nodes: readonly MapNode[], edges: readonly MapEdge[], maximum: number, select?: MapPolicySelection): PolicyPlan;
22
28
  export declare const POLICY_QUERY: string;
23
29
  export declare const POLICY_COLUMNS: readonly ['key', 'bindingProof', 'identity', 'resource', 'identityLink', 'resourceLink', 'principal', 'bucket', 'principalAccount', 'bucketAccount', 'principalOwnership', 'bucketOwnership', 'collection', 'principalMember', 'bucketMember', 'candidate'];
24
30
  export declare function policyParameters(plan: PolicyPlan): readonly Record<string, unknown>[];
25
31
  export declare function safePolicyRow(raw: unknown): JsonObject;
26
32
  /** Pure classification: no positive candidate survives a malformed endpoint or stale receipt. */
27
33
  export declare function policyEvidence(plan: PolicyPlan, rows: readonly JsonObject[], now: number, maximumAge: number): readonly MapPolicyEvidence[];
34
+ /** Buckets listed per identity before the comparison reports `truncated`. */
35
+ export declare const IDENTITY_SCOPE_BUCKETS = 500;
36
+ interface ScopeProbe {
37
+ readonly key: number;
38
+ readonly identity: MapNode;
39
+ readonly accountId: string;
40
+ readonly partition: string;
41
+ readonly principalArn: string;
42
+ readonly principalLabel: 'AWSRole' | 'AWSUser';
43
+ readonly collectionId: string;
44
+ readonly proof: Readonly<Record<string, string | null>>;
45
+ }
46
+ /** One execution binding's comparison against its identity's probe. */
47
+ interface ScopeLink {
48
+ readonly record: number;
49
+ readonly probe: number;
50
+ /** Buckets this tool's live grants bind as a whole bucket. */
51
+ readonly bound: ReadonlySet<string>;
52
+ /** Buckets bound only as an object or prefix: a bucket-level candidate may reach further. */
53
+ readonly partial: ReadonlySet<string>;
54
+ }
55
+ export interface IdentityScopePlan {
56
+ readonly records: readonly MapIdentityScope[];
57
+ /** One probe per distinct identity; several tools sharing a role share its probe. */
58
+ readonly probes: readonly ScopeProbe[];
59
+ readonly links: readonly ScopeLink[];
60
+ /** Only these bucket names are read, or every collected bucket when null. */
61
+ readonly buckets: readonly string[] | null;
62
+ /** Distinct buckets kept per identity before it reports `truncated`. */
63
+ readonly bucketLimit: number;
64
+ /** Rows read per identity; one past the expected maximum marks a longer list. */
65
+ readonly candidateLimit: number;
66
+ }
67
+ /**
68
+ * One record per execution binding; a probe per distinct, unambiguous, resolved static AWS
69
+ * identity. Past `maximum` identities the rest report `not_evaluated` instead of failing the
70
+ * read. `buckets` limits the read to those bucket names (the reach check behind
71
+ * `list_resources`). Grants count only on live tasks at `observedAt` (the snapshot time).
72
+ */
73
+ export declare function identityScopePlan(companyId: string, tenantId: string, nodes: readonly MapNode[], edges: readonly MapEdge[], maximum: number, buckets?: readonly string[] | null, observedAt?: number): IdentityScopePlan;
74
+ export declare const IDENTITY_SCOPE_QUERY: string;
75
+ export declare const IDENTITY_SCOPE_COLUMNS: readonly ['key', 'executionProof', 'identity', 'identityLink', 'principal', 'principalAccount', 'principalOwnership', 'collection', 'principalMember', 'candidates'];
76
+ export declare function identityScopeParameters(plan: IdentityScopePlan): readonly Record<string, unknown>[];
77
+ export declare function safeIdentityScopeRow(raw: unknown): JsonObject;
78
+ export declare function identityScopeEvidence(plan: IdentityScopePlan, rows: readonly JsonObject[], now: number, maximumAge: number,
79
+ /** For a team-scoped reader: the only bucket names it may see; null for an all-teams reader. */
80
+ readable?: ReadonlySet<string> | null): readonly MapIdentityScope[];
28
81
  export {};