@kici-dev/shared 0.6.1 → 0.8.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/cold-store/cold-store.js +4 -4
- package/dist/cold-store/index.d.ts +1 -1
- package/dist/cold-store/index.js +2 -2
- package/dist/cold-store/metrics.d.ts +17 -1
- package/dist/cold-store/metrics.js +17 -2
- package/dist/env/define-env.d.ts +14 -1
- package/dist/env/define-env.js +12 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/net/nftables.d.ts +356 -0
- package/dist/net/nftables.js +587 -0
- package/dist/net/nftables.test.d.ts +2 -0
- package/dist/net.d.ts +10 -0
- package/dist/net.js +3 -0
- package/package.json +7 -3
- package/sbom.spdx.json +25 -25
|
@@ -5,7 +5,7 @@ import { coldDaysToBucket, isLongerColdRetention } from "./bucket.js";
|
|
|
5
5
|
import { computeChunkId } from "./chunk-id.js";
|
|
6
6
|
import { decodeChunk, encodeChunk } from "./chunk-encoder.js";
|
|
7
7
|
import { parseManifest, serializeManifest } from "./manifest.js";
|
|
8
|
-
import { coldStoreArchiveBytesTotal, coldStoreArchiveCyclesTotal, coldStoreArchiveDurationSeconds, coldStoreArchiveRowsTotal, coldStorePurgeBytesTotal, coldStorePurgeChunksTotal, coldStorePurgeDurationSeconds, coldStoreRehydrateBytesTotal, coldStoreRehydrateDurationSeconds, coldStoreRehydrateRequestsTotal, coldStoreReplayDurationSeconds, coldStoreReplayRowsTotal, coldStoreVerifyFailuresTotal } from "./metrics.js";
|
|
8
|
+
import { ColdStoreCycleResult, coldStoreArchiveBytesTotal, coldStoreArchiveCyclesTotal, coldStoreArchiveDurationSeconds, coldStoreArchiveRowsTotal, coldStorePurgeBytesTotal, coldStorePurgeChunksTotal, coldStorePurgeDurationSeconds, coldStoreRehydrateBytesTotal, coldStoreRehydrateDurationSeconds, coldStoreRehydrateRequestsTotal, coldStoreReplayDurationSeconds, coldStoreReplayRowsTotal, coldStoreVerifyFailuresTotal } from "./metrics.js";
|
|
9
9
|
import { resolveTableConfig } from "./config.js";
|
|
10
10
|
import { sha256 } from "@kici-dev/core";
|
|
11
11
|
import { DeleteObjectCommand, GetObjectCommand, HeadObjectCommand, ListObjectsV2Command, PutObjectCommand } from "@aws-sdk/client-s3";
|
|
@@ -154,14 +154,14 @@ var BaseColdStore = class {
|
|
|
154
154
|
summary.skipped.disabled += 1;
|
|
155
155
|
coldStoreArchiveCyclesTotal().add(1, {
|
|
156
156
|
db: this.db,
|
|
157
|
-
result:
|
|
157
|
+
result: ColdStoreCycleResult.enum.disabled
|
|
158
158
|
});
|
|
159
159
|
return summary;
|
|
160
160
|
}
|
|
161
161
|
if (this.adapters.size === 0) {
|
|
162
162
|
coldStoreArchiveCyclesTotal().add(1, {
|
|
163
163
|
db: this.db,
|
|
164
|
-
result:
|
|
164
|
+
result: ColdStoreCycleResult.enum.no_tables
|
|
165
165
|
});
|
|
166
166
|
summary.skipped.no_tables += 1;
|
|
167
167
|
this.log("info", "cold-store cycle: no adapters registered", {
|
|
@@ -178,7 +178,7 @@ var BaseColdStore = class {
|
|
|
178
178
|
await this.processAdapter(adapter, summary);
|
|
179
179
|
summary.tablesProcessed += 1;
|
|
180
180
|
}
|
|
181
|
-
const cycleResult = summary.rowsFailed > 0 ?
|
|
181
|
+
const cycleResult = summary.rowsFailed > 0 ? ColdStoreCycleResult.enum.failure : summary.chunksWritten > 0 ? ColdStoreCycleResult.enum.success : ColdStoreCycleResult.enum.no_tables;
|
|
182
182
|
coldStoreArchiveCyclesTotal().add(1, {
|
|
183
183
|
db: this.db,
|
|
184
184
|
result: cycleResult
|
|
@@ -14,7 +14,7 @@ export { computeChunkId } from './chunk-id.js';
|
|
|
14
14
|
export { decodeChunk, encodeChunk, type DecodeChunkArgs, type EncodeChunkArgs, type EncodedChunk, } from './chunk-encoder.js';
|
|
15
15
|
export { parseManifest, serializeManifest } from './manifest.js';
|
|
16
16
|
export { DEFAULT_TABLE_CONFIG, resolveTableConfig, type ColdStoreConfig, type ColdStoreTableConfig, } from './config.js';
|
|
17
|
-
export { coldStoreArchiveBytesTotal, coldStoreArchiveCyclesTotal, coldStoreArchiveDurationSeconds, coldStoreArchiveRowsTotal, coldStorePurgeBytesTotal, coldStorePurgeChunksTotal, coldStorePurgeDurationSeconds, coldStoreRehydrateBytesTotal, coldStoreRehydrateDurationSeconds, coldStoreRehydrateRequestsTotal, coldStoreReplayDurationSeconds, coldStoreReplayRowsTotal, coldStoreVerifyFailuresTotal, } from './metrics.js';
|
|
17
|
+
export { ColdStoreCycleResult, coldStoreArchiveBytesTotal, coldStoreArchiveCyclesTotal, coldStoreArchiveDurationSeconds, coldStoreArchiveRowsTotal, coldStorePurgeBytesTotal, coldStorePurgeChunksTotal, coldStorePurgeDurationSeconds, coldStoreRehydrateBytesTotal, coldStoreRehydrateDurationSeconds, coldStoreRehydrateRequestsTotal, coldStoreReplayDurationSeconds, coldStoreReplayRowsTotal, coldStoreVerifyFailuresTotal, } from './metrics.js';
|
|
18
18
|
export { type ChunkCommitMetadata, type EligiblePartition, type TableAdapter, } from './table-adapter.js';
|
|
19
19
|
export { type ArchiveCycleSummary, type ChunkManifest, type ColdRetention } from './types.js';
|
|
20
20
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/cold-store/index.js
CHANGED
|
@@ -4,8 +4,8 @@ import { COLD_BUCKET_NAMES, coldDaysToBucket, isLongerColdRetention } from "./bu
|
|
|
4
4
|
import { computeChunkId } from "./chunk-id.js";
|
|
5
5
|
import { decodeChunk, encodeChunk } from "./chunk-encoder.js";
|
|
6
6
|
import { parseManifest, serializeManifest } from "./manifest.js";
|
|
7
|
-
import { coldStoreArchiveBytesTotal, coldStoreArchiveCyclesTotal, coldStoreArchiveDurationSeconds, coldStoreArchiveRowsTotal, coldStorePurgeBytesTotal, coldStorePurgeChunksTotal, coldStorePurgeDurationSeconds, coldStoreRehydrateBytesTotal, coldStoreRehydrateDurationSeconds, coldStoreRehydrateRequestsTotal, coldStoreReplayDurationSeconds, coldStoreReplayRowsTotal, coldStoreVerifyFailuresTotal } from "./metrics.js";
|
|
7
|
+
import { ColdStoreCycleResult, coldStoreArchiveBytesTotal, coldStoreArchiveCyclesTotal, coldStoreArchiveDurationSeconds, coldStoreArchiveRowsTotal, coldStorePurgeBytesTotal, coldStorePurgeChunksTotal, coldStorePurgeDurationSeconds, coldStoreRehydrateBytesTotal, coldStoreRehydrateDurationSeconds, coldStoreRehydrateRequestsTotal, coldStoreReplayDurationSeconds, coldStoreReplayRowsTotal, coldStoreVerifyFailuresTotal } from "./metrics.js";
|
|
8
8
|
import { DEFAULT_TABLE_CONFIG, resolveTableConfig } from "./config.js";
|
|
9
9
|
import { BaseColdStore } from "./cold-store.js";
|
|
10
10
|
import { ChunkLru } from "./lru.js";
|
|
11
|
-
export { BaseColdStore, COLD_BUCKET_NAMES, ChunkLru, DEFAULT_TABLE_CONFIG, chunkObjectKey, coldDaysToBucket, coldStoreArchiveBytesTotal, coldStoreArchiveCyclesTotal, coldStoreArchiveDurationSeconds, coldStoreArchiveRowsTotal, coldStorePurgeBytesTotal, coldStorePurgeChunksTotal, coldStorePurgeDurationSeconds, coldStoreRehydrateBytesTotal, coldStoreRehydrateDurationSeconds, coldStoreRehydrateRequestsTotal, coldStoreReplayDurationSeconds, coldStoreReplayRowsTotal, coldStoreVerifyFailuresTotal, computeChunkId, decodeChunk, encodeChunk, encodeKeySegment, isLongerColdRetention, parseManifest, resolveTableConfig, serializeManifest, tablePrefix, tenantDayBucketPrefix, tenantDayPrefix };
|
|
11
|
+
export { BaseColdStore, COLD_BUCKET_NAMES, ChunkLru, ColdStoreCycleResult, DEFAULT_TABLE_CONFIG, chunkObjectKey, coldDaysToBucket, coldStoreArchiveBytesTotal, coldStoreArchiveCyclesTotal, coldStoreArchiveDurationSeconds, coldStoreArchiveRowsTotal, coldStorePurgeBytesTotal, coldStorePurgeChunksTotal, coldStorePurgeDurationSeconds, coldStoreRehydrateBytesTotal, coldStoreRehydrateDurationSeconds, coldStoreRehydrateRequestsTotal, coldStoreReplayDurationSeconds, coldStoreReplayRowsTotal, coldStoreVerifyFailuresTotal, computeChunkId, decodeChunk, encodeChunk, encodeKeySegment, isLongerColdRetention, parseManifest, resolveTableConfig, serializeManifest, tablePrefix, tenantDayBucketPrefix, tenantDayPrefix };
|
|
@@ -28,12 +28,28 @@
|
|
|
28
28
|
* guarantees we resolve AFTER telemetry is wired up.
|
|
29
29
|
*/
|
|
30
30
|
import type { Counter, Histogram } from '@opentelemetry/api';
|
|
31
|
+
import { z } from 'zod';
|
|
32
|
+
/**
|
|
33
|
+
* The `result` label of {@link coldStoreArchiveCyclesTotal}: what one
|
|
34
|
+
* `runArchiveCycle()` tick concluded. `disabled` and `no_tables` are the
|
|
35
|
+
* cycle stopping before any adapter runs (config gate off / nothing
|
|
36
|
+
* registered); `success` wrote at least one chunk; `no_tables` is also what a
|
|
37
|
+
* complete pass reports when every adapter ran and none had a row to move;
|
|
38
|
+
* `failure` had a row fail to archive.
|
|
39
|
+
*/
|
|
40
|
+
export declare const ColdStoreCycleResult: z.ZodEnum<{
|
|
41
|
+
disabled: "disabled";
|
|
42
|
+
failure: "failure";
|
|
43
|
+
no_tables: "no_tables";
|
|
44
|
+
success: "success";
|
|
45
|
+
}>;
|
|
46
|
+
export type ColdStoreCycleResult = z.infer<typeof ColdStoreCycleResult>;
|
|
31
47
|
/**
|
|
32
48
|
* Cycles that `runArchiveCycle()` has completed, labeled by outcome.
|
|
33
49
|
* Always incremented by 1 on every tick — guaranteed-visible proof
|
|
34
50
|
* that the cold-store subsystem is registered and running.
|
|
35
51
|
*
|
|
36
|
-
* `result`
|
|
52
|
+
* `result` is a {@link ColdStoreCycleResult}.
|
|
37
53
|
*/
|
|
38
54
|
export declare function coldStoreArchiveCyclesTotal(): Counter;
|
|
39
55
|
/**
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import "../rolldown-runtime-ClRpJifh.js";
|
|
2
2
|
import { createMeter } from "../telemetry/metrics.js";
|
|
3
|
+
import { z } from "zod";
|
|
3
4
|
//#region src/cold-store/metrics.ts
|
|
4
5
|
let _meter;
|
|
5
6
|
function meter() {
|
|
@@ -20,11 +21,25 @@ let _purgeChunksTotal;
|
|
|
20
21
|
let _purgeBytesTotal;
|
|
21
22
|
let _purgeDurationSeconds;
|
|
22
23
|
/**
|
|
24
|
+
* The `result` label of {@link coldStoreArchiveCyclesTotal}: what one
|
|
25
|
+
* `runArchiveCycle()` tick concluded. `disabled` and `no_tables` are the
|
|
26
|
+
* cycle stopping before any adapter runs (config gate off / nothing
|
|
27
|
+
* registered); `success` wrote at least one chunk; `no_tables` is also what a
|
|
28
|
+
* complete pass reports when every adapter ran and none had a row to move;
|
|
29
|
+
* `failure` had a row fail to archive.
|
|
30
|
+
*/
|
|
31
|
+
const ColdStoreCycleResult = z.enum([
|
|
32
|
+
"disabled",
|
|
33
|
+
"no_tables",
|
|
34
|
+
"success",
|
|
35
|
+
"failure"
|
|
36
|
+
]);
|
|
37
|
+
/**
|
|
23
38
|
* Cycles that `runArchiveCycle()` has completed, labeled by outcome.
|
|
24
39
|
* Always incremented by 1 on every tick — guaranteed-visible proof
|
|
25
40
|
* that the cold-store subsystem is registered and running.
|
|
26
41
|
*
|
|
27
|
-
* `result`
|
|
42
|
+
* `result` is a {@link ColdStoreCycleResult}.
|
|
28
43
|
*/
|
|
29
44
|
function coldStoreArchiveCyclesTotal() {
|
|
30
45
|
if (!_archiveCyclesTotal) _archiveCyclesTotal = meter().createCounter("cold_store_archive_cycles_total", { description: "Completed archive cycles by db and outcome" });
|
|
@@ -174,6 +189,6 @@ function coldStorePurgeDurationSeconds() {
|
|
|
174
189
|
return _purgeDurationSeconds;
|
|
175
190
|
}
|
|
176
191
|
//#endregion
|
|
177
|
-
export { coldStoreArchiveBytesTotal, coldStoreArchiveCyclesTotal, coldStoreArchiveDurationSeconds, coldStoreArchiveRowsTotal, coldStorePurgeBytesTotal, coldStorePurgeChunksTotal, coldStorePurgeDurationSeconds, coldStoreRehydrateBytesTotal, coldStoreRehydrateDurationSeconds, coldStoreRehydrateRequestsTotal, coldStoreReplayDurationSeconds, coldStoreReplayRowsTotal, coldStoreVerifyFailuresTotal };
|
|
192
|
+
export { ColdStoreCycleResult, coldStoreArchiveBytesTotal, coldStoreArchiveCyclesTotal, coldStoreArchiveDurationSeconds, coldStoreArchiveRowsTotal, coldStorePurgeBytesTotal, coldStorePurgeChunksTotal, coldStorePurgeDurationSeconds, coldStoreRehydrateBytesTotal, coldStoreRehydrateDurationSeconds, coldStoreRehydrateRequestsTotal, coldStoreReplayDurationSeconds, coldStoreReplayRowsTotal, coldStoreVerifyFailuresTotal };
|
|
178
193
|
|
|
179
194
|
//# sourceMappingURL=metrics.js.map
|
package/dist/env/define-env.d.ts
CHANGED
|
@@ -122,7 +122,10 @@ export declare function defineEnv<TShape extends z.ZodRawShape>(opts: DefineEnvO
|
|
|
122
122
|
*
|
|
123
123
|
* Keep this list small and well-justified. Every addition is a typo we can
|
|
124
124
|
* no longer catch, so only list things that are (a) actually set in the
|
|
125
|
-
* wild by our own tooling and (b) could never be a config typo.
|
|
125
|
+
* wild by our own tooling and (b) could never be a config typo. A variable
|
|
126
|
+
* only one service reads belongs in that service's `extraKnown` instead —
|
|
127
|
+
* `KICI_CONFIG` is the reference case, allowlisted at the orchestrator's own
|
|
128
|
+
* call site so the agent and Platform catchers still reject it.
|
|
126
129
|
*/
|
|
127
130
|
export declare const RESERVED_NON_SCHEMA_KICI_VARS: readonly string[];
|
|
128
131
|
/**
|
|
@@ -176,6 +179,16 @@ export interface ValidateUnknownKiciVarsOptions {
|
|
|
176
179
|
warnOnly?: boolean;
|
|
177
180
|
/** Logger callback for warn-mode (defaults to `console.warn`). */
|
|
178
181
|
onWarn?: (msg: string) => void;
|
|
182
|
+
/**
|
|
183
|
+
* Advice text for names the caller recognises but does not accept — a
|
|
184
|
+
* documented variable that belongs to some other layer. A rejected name
|
|
185
|
+
* found here is reported with its advice instead of a Levenshtein guess,
|
|
186
|
+
* which finds nothing for exactly these names because they are not near
|
|
187
|
+
* misses of a schema name.
|
|
188
|
+
*
|
|
189
|
+
* This changes the message only. A name listed here is still rejected.
|
|
190
|
+
*/
|
|
191
|
+
notStartupVars?: Record<string, string>;
|
|
179
192
|
}
|
|
180
193
|
/**
|
|
181
194
|
* Inspect `env` for KICI_* keys that are not in `known`. Throws (or warns,
|
package/dist/env/define-env.js
CHANGED
|
@@ -241,7 +241,10 @@ function suggestClosest(name, candidates) {
|
|
|
241
241
|
*
|
|
242
242
|
* Keep this list small and well-justified. Every addition is a typo we can
|
|
243
243
|
* no longer catch, so only list things that are (a) actually set in the
|
|
244
|
-
* wild by our own tooling and (b) could never be a config typo.
|
|
244
|
+
* wild by our own tooling and (b) could never be a config typo. A variable
|
|
245
|
+
* only one service reads belongs in that service's `extraKnown` instead —
|
|
246
|
+
* `KICI_CONFIG` is the reference case, allowlisted at the orchestrator's own
|
|
247
|
+
* call site so the agent and Platform catchers still reject it.
|
|
245
248
|
*/
|
|
246
249
|
const RESERVED_NON_SCHEMA_KICI_VARS = [
|
|
247
250
|
"KICI_CACHE",
|
|
@@ -305,21 +308,27 @@ function validateUnknownKiciVars(known, options = {}, env = process.env) {
|
|
|
305
308
|
...options.extraKnown ?? [],
|
|
306
309
|
...RESERVED_NON_SCHEMA_KICI_VARS
|
|
307
310
|
]);
|
|
311
|
+
const advice = options.notStartupVars ?? {};
|
|
308
312
|
const unknown = [];
|
|
309
313
|
for (const key of Object.keys(env)) {
|
|
310
314
|
if (!key.startsWith("KICI_")) continue;
|
|
311
315
|
if (knownSet.has(key)) continue;
|
|
312
316
|
if (RESERVED_NON_SCHEMA_KICI_PREFIXES.some((p) => key.startsWith(p))) continue;
|
|
313
317
|
if (RESERVED_NON_SCHEMA_KICI_SUFFIXES.some((s) => key.endsWith(s) || key.includes(`${s}_`))) continue;
|
|
318
|
+
const note = advice[key];
|
|
314
319
|
unknown.push({
|
|
315
320
|
name: key,
|
|
316
|
-
|
|
321
|
+
note,
|
|
322
|
+
suggestion: note ? void 0 : suggestClosest(key, [...knownSet])
|
|
317
323
|
});
|
|
318
324
|
}
|
|
319
325
|
if (unknown.length === 0) return;
|
|
320
326
|
const header = `Unknown KICI_* env var(s) detected — refusing to start.
|
|
321
327
|
Set KICI_DEV=true to downgrade this check to a warning.
|
|
322
|
-
Unknown vars:\n${unknown.map(({ name, suggestion }) =>
|
|
328
|
+
Unknown vars:\n${unknown.map(({ name, suggestion, note }) => {
|
|
329
|
+
if (note) return ` - ${name} (${note})`;
|
|
330
|
+
return suggestion ? ` - ${name} (did you mean ${suggestion}?)` : ` - ${name} (no close match in the schema)`;
|
|
331
|
+
}).join("\n")}`;
|
|
323
332
|
if (options.warnOnly ?? env.KICI_DEV === "true") {
|
|
324
333
|
(options.onWarn ?? console.warn)(header);
|
|
325
334
|
return;
|
package/dist/index.d.ts
CHANGED
|
@@ -15,5 +15,5 @@ export { setupGracefulShutdown, type ShutdownStep, type ShutdownLogger, type Shu
|
|
|
15
15
|
export { validateRequiredTools, type ToolRequirement } from './tool-check.js';
|
|
16
16
|
export { kiciTmpBase, kiciMkdtemp } from './tmp-dir.js';
|
|
17
17
|
export { createS3Client, type CreateS3ClientOptions, type SharedS3Config } from './s3-client.js';
|
|
18
|
-
export { BaseColdStore, ChunkLru, COLD_BUCKET_NAMES, DEFAULT_TABLE_CONFIG, chunkObjectKey, coldDaysToBucket, coldStoreArchiveBytesTotal, coldStoreArchiveCyclesTotal, coldStoreArchiveDurationSeconds, coldStoreArchiveRowsTotal, coldStorePurgeBytesTotal, coldStorePurgeChunksTotal, coldStorePurgeDurationSeconds, coldStoreRehydrateBytesTotal, coldStoreRehydrateDurationSeconds, coldStoreRehydrateRequestsTotal, coldStoreReplayDurationSeconds, coldStoreReplayRowsTotal, coldStoreVerifyFailuresTotal, computeChunkId, decodeChunk, encodeChunk, encodeKeySegment, isLongerColdRetention, parseManifest, resolveTableConfig, serializeManifest, tablePrefix, tenantDayBucketPrefix, tenantDayPrefix, type ArchiveCycleSummary, type BaseColdStoreDeps, type ChunkCommitMetadata, type ChunkLruOptions, type ChunkManifest, type ColdBucketName, type ColdRetention, type ColdStore, type ColdStoreConfig, type ColdStoreFetchRangeArgs, type ColdStoreReplayChunkArgs, type ColdStoreReplayResult, type ColdStoreReplayRowArgs, type ColdStoreTableConfig, type DbKind, type DecodeChunkArgs, type EligiblePartition, type EncodeChunkArgs, type EncodedChunk, type PurgeableChunk, type PurgeChunkResult, type PurgeExpiredChunksOpts, type PurgeExpiredChunksSummary, type TableAdapter, } from './cold-store/index.js';
|
|
18
|
+
export { BaseColdStore, ChunkLru, COLD_BUCKET_NAMES, DEFAULT_TABLE_CONFIG, chunkObjectKey, coldDaysToBucket, coldStoreArchiveBytesTotal, ColdStoreCycleResult, coldStoreArchiveCyclesTotal, coldStoreArchiveDurationSeconds, coldStoreArchiveRowsTotal, coldStorePurgeBytesTotal, coldStorePurgeChunksTotal, coldStorePurgeDurationSeconds, coldStoreRehydrateBytesTotal, coldStoreRehydrateDurationSeconds, coldStoreRehydrateRequestsTotal, coldStoreReplayDurationSeconds, coldStoreReplayRowsTotal, coldStoreVerifyFailuresTotal, computeChunkId, decodeChunk, encodeChunk, encodeKeySegment, isLongerColdRetention, parseManifest, resolveTableConfig, serializeManifest, tablePrefix, tenantDayBucketPrefix, tenantDayPrefix, type ArchiveCycleSummary, type BaseColdStoreDeps, type ChunkCommitMetadata, type ChunkLruOptions, type ChunkManifest, type ColdBucketName, type ColdRetention, type ColdStore, type ColdStoreConfig, type ColdStoreFetchRangeArgs, type ColdStoreReplayChunkArgs, type ColdStoreReplayResult, type ColdStoreReplayRowArgs, type ColdStoreTableConfig, type DbKind, type DecodeChunkArgs, type EligiblePartition, type EncodeChunkArgs, type EncodedChunk, type PurgeableChunk, type PurgeChunkResult, type PurgeExpiredChunksOpts, type PurgeExpiredChunksSummary, type TableAdapter, } from './cold-store/index.js';
|
|
19
19
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -22,10 +22,10 @@ import { COLD_BUCKET_NAMES, coldDaysToBucket, isLongerColdRetention } from "./co
|
|
|
22
22
|
import { computeChunkId } from "./cold-store/chunk-id.js";
|
|
23
23
|
import { decodeChunk, encodeChunk } from "./cold-store/chunk-encoder.js";
|
|
24
24
|
import { parseManifest, serializeManifest } from "./cold-store/manifest.js";
|
|
25
|
-
import { coldStoreArchiveBytesTotal, coldStoreArchiveCyclesTotal, coldStoreArchiveDurationSeconds, coldStoreArchiveRowsTotal, coldStorePurgeBytesTotal, coldStorePurgeChunksTotal, coldStorePurgeDurationSeconds, coldStoreRehydrateBytesTotal, coldStoreRehydrateDurationSeconds, coldStoreRehydrateRequestsTotal, coldStoreReplayDurationSeconds, coldStoreReplayRowsTotal, coldStoreVerifyFailuresTotal } from "./cold-store/metrics.js";
|
|
25
|
+
import { ColdStoreCycleResult, coldStoreArchiveBytesTotal, coldStoreArchiveCyclesTotal, coldStoreArchiveDurationSeconds, coldStoreArchiveRowsTotal, coldStorePurgeBytesTotal, coldStorePurgeChunksTotal, coldStorePurgeDurationSeconds, coldStoreRehydrateBytesTotal, coldStoreRehydrateDurationSeconds, coldStoreRehydrateRequestsTotal, coldStoreReplayDurationSeconds, coldStoreReplayRowsTotal, coldStoreVerifyFailuresTotal } from "./cold-store/metrics.js";
|
|
26
26
|
import { DEFAULT_TABLE_CONFIG, resolveTableConfig } from "./cold-store/config.js";
|
|
27
27
|
import { BaseColdStore } from "./cold-store/cold-store.js";
|
|
28
28
|
import { ChunkLru } from "./cold-store/lru.js";
|
|
29
29
|
import "./cold-store/index.js";
|
|
30
30
|
export * from "@kici-dev/core";
|
|
31
|
-
export { AgentDeliveryMode, AgentPlatform, BaseColdStore, BundleChunkAssembler, COLD_BUCKET_NAMES, ChunkLru, ChunkRequestWaiter, DEFAULT_TABLE_CONFIG, FLEET_CHUNK_BYTES, MAX_LOG_BYTES, MIGRATION_HASH_TABLE, REGISTERABLE_TRIGGER_TYPES, RingBuffer, addLogsToArchive, chunkBuffer, chunkObjectKey, clearDispatchQueueDirect, coldDaysToBucket, coldStoreArchiveBytesTotal, coldStoreArchiveCyclesTotal, coldStoreArchiveDurationSeconds, coldStoreArchiveRowsTotal, coldStorePurgeBytesTotal, coldStorePurgeChunksTotal, coldStorePurgeDurationSeconds, coldStoreRehydrateBytesTotal, coldStoreRehydrateDurationSeconds, coldStoreRehydrateRequestsTotal, coldStoreReplayDurationSeconds, coldStoreReplayRowsTotal, coldStoreVerifyFailuresTotal, collectRuntimeMetricNames, computeChunkId, computeMigrationsHash, createContextTemplateDirect, createDb, createDbRole, createHealthRoutes, createMeter, createMetricsRoutes, createPool, createReadOnlyDbUser, createS3Client, decodeChunk, decrypt, deleteContextDirect, deriveKey, dropAndCreateDatabase, emitKiciEventDirect, encodeChunk, encodeKeySegment, encrypt, ensureDatabase, generateMasterKey, getPrometheusExporter, getReconnectDelay, initTelemetry, isLongerColdRetention, isPgUniqueViolation, isPoolAcquireTimeout, isSchemaCurrent, kiciMkdtemp, kiciTmpBase, listCheckRunTrackingDirect, listContextsDirect, listExecutionRunsDirect, listQueueDirect, listRegistrationsDirect, maskDatabaseUrl, parseDatabaseUrl, parseManifest, prunePeerCredentialsDirect, purgeContextsDirect, purgeScopedSecretsDirect, purgeSecretBackendsDirect, purgeStaleExecutionDirect, purgeStaleSourcesDirect, readStoredMigrationContentHash, registerWorkflowManualDirect, resetRaftStateDirect, resolveTableConfig, seedContextBindingDirect, seedContextDirect, serializeManifest, setContextPolicyDirect, setContextSecretDirect, setupGracefulShutdown, showContextDirect, showExecutionRunDirect, showQueueEntryDirect, showRegistrationDirect, splitAgentPlatform, storeMigrationContentHash, tablePrefix, tenantDayBucketPrefix, tenantDayPrefix, validateRequiredTools };
|
|
31
|
+
export { AgentDeliveryMode, AgentPlatform, BaseColdStore, BundleChunkAssembler, COLD_BUCKET_NAMES, ChunkLru, ChunkRequestWaiter, ColdStoreCycleResult, DEFAULT_TABLE_CONFIG, FLEET_CHUNK_BYTES, MAX_LOG_BYTES, MIGRATION_HASH_TABLE, REGISTERABLE_TRIGGER_TYPES, RingBuffer, addLogsToArchive, chunkBuffer, chunkObjectKey, clearDispatchQueueDirect, coldDaysToBucket, coldStoreArchiveBytesTotal, coldStoreArchiveCyclesTotal, coldStoreArchiveDurationSeconds, coldStoreArchiveRowsTotal, coldStorePurgeBytesTotal, coldStorePurgeChunksTotal, coldStorePurgeDurationSeconds, coldStoreRehydrateBytesTotal, coldStoreRehydrateDurationSeconds, coldStoreRehydrateRequestsTotal, coldStoreReplayDurationSeconds, coldStoreReplayRowsTotal, coldStoreVerifyFailuresTotal, collectRuntimeMetricNames, computeChunkId, computeMigrationsHash, createContextTemplateDirect, createDb, createDbRole, createHealthRoutes, createMeter, createMetricsRoutes, createPool, createReadOnlyDbUser, createS3Client, decodeChunk, decrypt, deleteContextDirect, deriveKey, dropAndCreateDatabase, emitKiciEventDirect, encodeChunk, encodeKeySegment, encrypt, ensureDatabase, generateMasterKey, getPrometheusExporter, getReconnectDelay, initTelemetry, isLongerColdRetention, isPgUniqueViolation, isPoolAcquireTimeout, isSchemaCurrent, kiciMkdtemp, kiciTmpBase, listCheckRunTrackingDirect, listContextsDirect, listExecutionRunsDirect, listQueueDirect, listRegistrationsDirect, maskDatabaseUrl, parseDatabaseUrl, parseManifest, prunePeerCredentialsDirect, purgeContextsDirect, purgeScopedSecretsDirect, purgeSecretBackendsDirect, purgeStaleExecutionDirect, purgeStaleSourcesDirect, readStoredMigrationContentHash, registerWorkflowManualDirect, resetRaftStateDirect, resolveTableConfig, seedContextBindingDirect, seedContextDirect, serializeManifest, setContextPolicyDirect, setContextSecretDirect, setupGracefulShutdown, showContextDirect, showExecutionRunDirect, showQueueEntryDirect, showRegistrationDirect, splitAgentPlatform, storeMigrationContentHash, tablePrefix, tenantDayBucketPrefix, tenantDayPrefix, validateRequiredTools };
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Programmatic nftables rule management for agent network isolation.
|
|
3
|
+
*
|
|
4
|
+
* Manages RFC1918 + cloud metadata blocking rules per-interface (Firecracker/container)
|
|
5
|
+
* or per-UID (bare-metal). All operations use `nft` CLI via child_process.execFile.
|
|
6
|
+
*
|
|
7
|
+
* Table layout:
|
|
8
|
+
* table ip kici {
|
|
9
|
+
* chain forward { type filter hook forward priority 0; policy accept; }
|
|
10
|
+
* chain input { type filter hook input priority 0; policy accept; }
|
|
11
|
+
* chain output { type filter hook output priority 0; policy accept; }
|
|
12
|
+
* }
|
|
13
|
+
*
|
|
14
|
+
* `forward` and `input` answer different questions and both are needed. A
|
|
15
|
+
* packet a sandbox sends to one of the host's OWN addresses — a bridge gateway,
|
|
16
|
+
* the host's LAN address — is delivered on the input hook and never traverses
|
|
17
|
+
* forward, so a forward rule cannot see it. `forward` governs what a sandbox
|
|
18
|
+
* reaches THROUGH the host; `input` governs what it reaches ON the host.
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* Network policy controlling RFC1918 and internet access for the agents or job
|
|
22
|
+
* containers in one label set.
|
|
23
|
+
*
|
|
24
|
+
* Lives here rather than beside the scaler's own types because both the
|
|
25
|
+
* orchestrator's scaler (agent containers and Firecracker VMs) and the agent's
|
|
26
|
+
* container backend (nested job containers) build rules from it. The
|
|
27
|
+
* orchestrator re-exports it from `scaler/types.ts` so its call sites are
|
|
28
|
+
* unchanged.
|
|
29
|
+
*/
|
|
30
|
+
export interface NetworkPolicy {
|
|
31
|
+
/** CIDR ranges allowed as exceptions to the default RFC1918 block */
|
|
32
|
+
allowlist?: string[];
|
|
33
|
+
/** Block all outbound traffic except allowlisted ranges */
|
|
34
|
+
denyAll?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* What this source class may reach on the HOST itself, in the
|
|
37
|
+
* `<cidr|address|*>[:<port|*>]` vocabulary {@link parseHostAccess} reads.
|
|
38
|
+
* Everything else host-destined is dropped.
|
|
39
|
+
*
|
|
40
|
+
* Distinct from {@link allowlist}, which governs the `forward` hook and so
|
|
41
|
+
* answers what a sandbox reaches *through* the host. Leaving this undefined
|
|
42
|
+
* means the caller supplies its own class default; an empty array means
|
|
43
|
+
* "reach nothing on the host".
|
|
44
|
+
*/
|
|
45
|
+
hostAccess?: string[];
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* One parsed {@link NetworkPolicy.hostAccess} entry.
|
|
49
|
+
*
|
|
50
|
+
* `null` means "unconstrained" on both fields: a `daddr` of `null` matches
|
|
51
|
+
* every host address (nft omits the `ip daddr` clause), and a `port` of
|
|
52
|
+
* `null` matches every port.
|
|
53
|
+
*/
|
|
54
|
+
export interface HostAccessRule {
|
|
55
|
+
daddr: string | null;
|
|
56
|
+
port: number | null;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Parse one host-access entry.
|
|
60
|
+
*
|
|
61
|
+
* Grammar: `<cidr|address|*>` optionally followed by `:<port|*>`. A bare number
|
|
62
|
+
* is a port on any host address, which is the common case — an operator naming
|
|
63
|
+
* a host-local registry mirror knows its port, not the host's dynamic
|
|
64
|
+
* addresses.
|
|
65
|
+
*
|
|
66
|
+
* Hostnames are rejected on purpose: nftables matches addresses, so resolving a
|
|
67
|
+
* name at rule-build time produces a rule that goes stale silently the next
|
|
68
|
+
* time the name moves.
|
|
69
|
+
*
|
|
70
|
+
* @throws Error naming the entry and what is wrong with it
|
|
71
|
+
*/
|
|
72
|
+
export declare function parseHostAccess(entry: string): HostAccessRule;
|
|
73
|
+
/**
|
|
74
|
+
* Build the host-access rules for one identifier, in final head-to-tail order:
|
|
75
|
+
* the conntrack exception, every accept, then one terminal drop.
|
|
76
|
+
*
|
|
77
|
+
* The conntrack rule leads because the chain is keyed on the sandbox as the
|
|
78
|
+
* SOURCE, so it also sees the reply leg of a connection the HOST opened toward
|
|
79
|
+
* the sandbox — a readiness probe, a metrics scrape. Without the exception
|
|
80
|
+
* those replies fall through to the terminal drop and the host's own connection
|
|
81
|
+
* dies as an opaque timeout. It widens nothing a sandbox can initiate:
|
|
82
|
+
* `established` is reached only by a flow whose first packet was already
|
|
83
|
+
* accepted, so a sandbox connecting to a port with no accept still has its SYN
|
|
84
|
+
* dropped and never reaches that state.
|
|
85
|
+
*
|
|
86
|
+
* A port-scoped entry emits an accept per protocol in
|
|
87
|
+
* {@link HOST_ACCESS_PROTOCOLS}. UDP is not optional: a container whose
|
|
88
|
+
* resolver is the bridge gateway — which is what rootful podman with
|
|
89
|
+
* aardvark-dns gives it — resolves over UDP, so a tcp-only accept on port 53
|
|
90
|
+
* leaves it unable to resolve any name.
|
|
91
|
+
*
|
|
92
|
+
* @returns one token list per rule, in final head-to-tail order
|
|
93
|
+
*/
|
|
94
|
+
export declare function buildHostAccessRuleOps(matchClause: string[], hostAccess: string[]): string[][];
|
|
95
|
+
/**
|
|
96
|
+
* Match mode for nftables isolation rules.
|
|
97
|
+
* - 'iifname': Match on input interface name (Firecracker TAP devices)
|
|
98
|
+
* - 'saddr': Match on source IP address (container backends)
|
|
99
|
+
*/
|
|
100
|
+
export type NftMatchMode = 'iifname' | 'saddr';
|
|
101
|
+
/** RFC1918 private address ranges. */
|
|
102
|
+
export declare const RFC1918_RANGES: string[];
|
|
103
|
+
/** Cloud metadata service range (AWS/GCP/Azure link-local). */
|
|
104
|
+
export declare const METADATA_RANGE = "169.254.0.0/16";
|
|
105
|
+
/**
|
|
106
|
+
* Subnet of the agent's `kici-jobs` bridge, on which the agent keys ONE drop set
|
|
107
|
+
* covering every nested job container — including one that does not exist yet.
|
|
108
|
+
*
|
|
109
|
+
* It lives here rather than beside the agent's own network constants because
|
|
110
|
+
* the Firecracker host provisioner has to recognise those rules to leave them
|
|
111
|
+
* alone, and the orchestrator carries `@kici-dev/agent` only as a devDependency.
|
|
112
|
+
* The agent module re-exports it, so its own call sites are unchanged.
|
|
113
|
+
*/
|
|
114
|
+
export declare const JOB_NETWORK_SUBNET = "172.31.0.0/16";
|
|
115
|
+
/**
|
|
116
|
+
* Options for nft command execution.
|
|
117
|
+
*/
|
|
118
|
+
interface NftOptions {
|
|
119
|
+
/**
|
|
120
|
+
* Wrap the `nft` invocation with `sudo -n` so non-root orchestrators (e.g.
|
|
121
|
+
* Pi user-mode systemd) can manage rules. Operators must have a NOPASSWD
|
|
122
|
+
* sudoers entry for /usr/sbin/nft. Default false.
|
|
123
|
+
*/
|
|
124
|
+
requireSudo?: boolean;
|
|
125
|
+
/**
|
|
126
|
+
* nftables table these rules live in. Defaults to {@link DEFAULT_NFT_TABLE}.
|
|
127
|
+
*
|
|
128
|
+
* The Firecracker backend exposes this as an operator knob so two
|
|
129
|
+
* coordinators on one host get disjoint tables; the per-VM rules must follow
|
|
130
|
+
* the same knob as the bridge baseline, or coordinator B's rules land in
|
|
131
|
+
* coordinator A's table and A's next provision wipes them.
|
|
132
|
+
*/
|
|
133
|
+
table?: string;
|
|
134
|
+
}
|
|
135
|
+
/** Table used when {@link NftOptions.table} is not set. */
|
|
136
|
+
export declare const DEFAULT_NFT_TABLE = "kici";
|
|
137
|
+
/**
|
|
138
|
+
* Validate that nftables is available and the process has NET_ADMIN capability.
|
|
139
|
+
* Attempts `nft list tables` -- if it fails:
|
|
140
|
+
* - ENOENT: nft binary not installed
|
|
141
|
+
* - EPERM: nft binary present but NET_ADMIN capability missing
|
|
142
|
+
* Throws with a clear error message in both cases.
|
|
143
|
+
*/
|
|
144
|
+
export declare function validateNftablesAvailability(opts?: NftOptions): Promise<void>;
|
|
145
|
+
/**
|
|
146
|
+
* Ensure the nftables table and the chains this module writes to exist.
|
|
147
|
+
* Idempotent -- safe to call multiple times.
|
|
148
|
+
*
|
|
149
|
+
* Every chain is verified individually. A bare "does the table exist?" check is
|
|
150
|
+
* not enough: the Firecracker host provisioner creates the table before this
|
|
151
|
+
* module ever runs, so a table with no `forward` chain satisfied the old early
|
|
152
|
+
* return — and then every `addIsolationRules` failed with nft's "No such file
|
|
153
|
+
* or directory", leaving every VM on that host with no isolation rules at all.
|
|
154
|
+
*
|
|
155
|
+
* @param opts - `table` selects the table; `requireBaselineChain` additionally
|
|
156
|
+
* ensures the regular {@link BASELINE_CHAIN} exists (Firecracker hosts, whose
|
|
157
|
+
* `forward` chain ends in a jump to it).
|
|
158
|
+
*/
|
|
159
|
+
export declare function ensureKiciTable(opts?: NftOptions & {
|
|
160
|
+
requireBaselineChain?: boolean;
|
|
161
|
+
}): Promise<void>;
|
|
162
|
+
/** Chain in the kici table that filters host-destined sandbox traffic. */
|
|
163
|
+
export declare const INPUT_CHAIN = "input";
|
|
164
|
+
/**
|
|
165
|
+
* Ensure the kici table and its `input` chain exist. Idempotent — `nft add` is
|
|
166
|
+
* a create-or-noop for both, so there is no check-then-create window.
|
|
167
|
+
*
|
|
168
|
+
* The chain's policy is `accept` because it is a base chain on the host's own
|
|
169
|
+
* input hook: everything the host itself receives passes through it, and a
|
|
170
|
+
* default-deny there would take the machine off the network. The deny lives in
|
|
171
|
+
* the per-identifier terminal drop {@link buildHostAccessRuleOps} emits.
|
|
172
|
+
*/
|
|
173
|
+
export declare function ensureKiciInputChain(opts?: NftOptions): Promise<void>;
|
|
174
|
+
/**
|
|
175
|
+
* Apply one identifier's host-access rules to the `input` chain, replacing
|
|
176
|
+
* whatever it had.
|
|
177
|
+
*
|
|
178
|
+
* The pre-clean is not an optimisation. Bridge networks recycle addresses, so a
|
|
179
|
+
* crash or a `kill -9` leaves the previous holder's accepts behind for the next
|
|
180
|
+
* container on that IP to inherit — which is the boundary this chain exists to
|
|
181
|
+
* hold. Applying without removing first would also stack a second terminal drop
|
|
182
|
+
* above the first run's accepts, shadowing every one of them.
|
|
183
|
+
*
|
|
184
|
+
* Rules are `insert`ed in reverse so the block lands at the chain head in the
|
|
185
|
+
* order {@link buildHostAccessRuleOps} returns: accepts first, terminal drop
|
|
186
|
+
* last. Inserting forwards would put the drop above the accepts and deny
|
|
187
|
+
* everything.
|
|
188
|
+
*/
|
|
189
|
+
export declare function addHostIsolationRules(identifier: string, hostAccess: string[], matchMode?: NftMatchMode, opts?: NftOptions): Promise<void>;
|
|
190
|
+
/**
|
|
191
|
+
* A table's `input` chain, as `nft -a list chain` prints it, or `null` when the
|
|
192
|
+
* table or the chain does not exist yet.
|
|
193
|
+
*
|
|
194
|
+
* Returning `null` rather than throwing keeps "the chain is not there" distinct
|
|
195
|
+
* from "the chain is there and holds nothing", which a caller deciding whether
|
|
196
|
+
* a rule set is already installed has to be able to tell apart.
|
|
197
|
+
*/
|
|
198
|
+
export declare function readInputChain(opts?: NftOptions): Promise<string | null>;
|
|
199
|
+
/**
|
|
200
|
+
* Remove one identifier's rules from the `input` chain.
|
|
201
|
+
*
|
|
202
|
+
* Best-effort: a missing chain, or a rule another path already deleted, must
|
|
203
|
+
* not abort a teardown.
|
|
204
|
+
*/
|
|
205
|
+
export declare function removeHostIsolationRules(identifier: string, opts?: NftOptions): Promise<void>;
|
|
206
|
+
/**
|
|
207
|
+
* Name of the regular (non-hooked) chain holding the host baseline rules.
|
|
208
|
+
*
|
|
209
|
+
* The Firecracker host provisioner puts its six source-scoped baseline rules
|
|
210
|
+
* here and reaches them with a `jump` appended as the `forward` chain's LAST
|
|
211
|
+
* rule. Two properties follow, and both are load-bearing:
|
|
212
|
+
*
|
|
213
|
+
* - **Per-VM rules always win.** They are inserted at the `forward` head, so
|
|
214
|
+
* every one of them is evaluated before the jump. A per-VM `accept`
|
|
215
|
+
* terminates the hook before the baseline can re-drop an allowlisted
|
|
216
|
+
* destination, and a per-VM `denyAll` drop is terminal before the
|
|
217
|
+
* baseline's blanket internet `accept` can let the packet out.
|
|
218
|
+
* - **A self-heal can rebuild the baseline without touching live VMs.** The
|
|
219
|
+
* provisioner flushes and refills only this chain, so it never has to
|
|
220
|
+
* `delete table` — which used to drop every running VM's isolation rules
|
|
221
|
+
* fail-open.
|
|
222
|
+
*
|
|
223
|
+
* A regular chain reached by `jump` rather than a second base chain at a lower
|
|
224
|
+
* priority: in netfilter an `accept` ends only the current base chain, so an
|
|
225
|
+
* allowlist accept in an earlier base chain would still be re-evaluated — and
|
|
226
|
+
* dropped — by the baseline's 10.0.0.0/8 rule in the later one.
|
|
227
|
+
*/
|
|
228
|
+
export declare const BASELINE_CHAIN = "baseline";
|
|
229
|
+
/**
|
|
230
|
+
* Build the per-identifier isolation rules in their final head-to-tail order.
|
|
231
|
+
*
|
|
232
|
+
* nftables is first-match-wins within a chain and `accept` is terminal, so the
|
|
233
|
+
* order below is the whole security property:
|
|
234
|
+
*
|
|
235
|
+
* 1. gateway accept
|
|
236
|
+
* 2. allowlisted CIDR accepts — ahead of the drops, so an allowlisted
|
|
237
|
+
* destination inside a dropped range (a 10.x registry endpoint behind the
|
|
238
|
+
* 10.0.0.0/8 drop) is accepted before the drop is evaluated
|
|
239
|
+
* 3. RFC1918 drops
|
|
240
|
+
* 4. cloud-metadata drop
|
|
241
|
+
* 5. `denyAll` drop
|
|
242
|
+
*
|
|
243
|
+
* {@link addIsolationRules} lands them in exactly this order by applying the
|
|
244
|
+
* list in REVERSE with `insert`, which puts the whole block at the chain head —
|
|
245
|
+
* ahead of the tail `jump` to {@link BASELINE_CHAIN}.
|
|
246
|
+
*
|
|
247
|
+
* @returns one token list per rule, in final head-to-tail order
|
|
248
|
+
*/
|
|
249
|
+
export declare function buildIsolationRuleOps(matchClause: string[], gatewayIp: string, networkPolicy?: NetworkPolicy): string[][];
|
|
250
|
+
/**
|
|
251
|
+
* Add network isolation rules for one identifier (a TAP interface name, or a
|
|
252
|
+
* container's source IP).
|
|
253
|
+
*
|
|
254
|
+
* Every rule is `insert`ed, applying {@link buildIsolationRuleOps} in reverse,
|
|
255
|
+
* so the block lands at the chain head in its documented order — ahead of any
|
|
256
|
+
* host baseline reached by a tail `jump`.
|
|
257
|
+
*
|
|
258
|
+
* @param identifier - Network interface name or source IP to match (e.g., "veth-abc123" or "172.30.0.5")
|
|
259
|
+
* @param gatewayIp - Gateway IP that must remain accessible (e.g., "10.0.0.1")
|
|
260
|
+
* @param networkPolicy - Optional policy with allowlist and denyAll settings
|
|
261
|
+
* @param matchMode - How to match traffic: 'iifname' for interface name (default), 'saddr' for source IP
|
|
262
|
+
*/
|
|
263
|
+
export declare function addIsolationRules(identifier: string, gatewayIp: string, networkPolicy?: NetworkPolicy, matchMode?: NftMatchMode, opts?: NftOptions): Promise<void>;
|
|
264
|
+
/**
|
|
265
|
+
* A table's `forward` chain, as `nft -a list chain` prints it, or `null` when
|
|
266
|
+
* the table or the chain does not exist yet.
|
|
267
|
+
*
|
|
268
|
+
* The text form is what {@link parseRuleHandles} reads, and it is also what a
|
|
269
|
+
* caller asking whether a rule set is ALREADY installed — rather than deleting
|
|
270
|
+
* it — compares against the rule text {@link buildIsolationRuleOps} emits.
|
|
271
|
+
* {@link listForwardRules} cannot answer for a subnet identifier: nft reports a
|
|
272
|
+
* subnet source as a prefix object, which its classifier treats as "not a
|
|
273
|
+
* per-agent rule" on purpose.
|
|
274
|
+
*
|
|
275
|
+
* Returning `null` rather than throwing keeps "the chain is not there" distinct
|
|
276
|
+
* from "the chain is there and holds nothing", exactly as {@link readInputChain}
|
|
277
|
+
* does for the sibling chain. The distinction is load-bearing in the install
|
|
278
|
+
* direction: a caller checking whether a rule set is present must read a missing
|
|
279
|
+
* chain as NOT installed and go install it, and a throw instead aborts the whole
|
|
280
|
+
* ensure path — which for the agent's job network degrades to a container on an
|
|
281
|
+
* unfiltered bridge.
|
|
282
|
+
*/
|
|
283
|
+
export declare function readForwardChain(opts?: NftOptions): Promise<string | null>;
|
|
284
|
+
/**
|
|
285
|
+
* Remove every isolation rule this identifier owns, in both the `forward` and
|
|
286
|
+
* the `input` chain. Called during agent cleanup, and as the pre-clean before a
|
|
287
|
+
* re-add.
|
|
288
|
+
*
|
|
289
|
+
* Both chains are swept because they are one boundary: an identifier is a
|
|
290
|
+
* container IP or a TAP name, addresses are recycled, and leaving the input
|
|
291
|
+
* chain's accepts behind hands the next tenant on that address the previous
|
|
292
|
+
* one's host reachability.
|
|
293
|
+
*
|
|
294
|
+
* Best-effort: logs errors but does not throw (cleanup must not block
|
|
295
|
+
* destruction).
|
|
296
|
+
*
|
|
297
|
+
* @param interfaceName - Network interface name or source IP whose rules should be removed
|
|
298
|
+
*/
|
|
299
|
+
export declare function removeIsolationRules(interfaceName: string, opts?: NftOptions): Promise<void>;
|
|
300
|
+
/**
|
|
301
|
+
* Re-export NftOptions type for use by callers (Firecracker / container backends)
|
|
302
|
+
* that need to pass the requireSudo flag through to these functions.
|
|
303
|
+
*/
|
|
304
|
+
export type { NftOptions };
|
|
305
|
+
/** One rule in the forward chain, as reported by `nft -j -a list chain`. */
|
|
306
|
+
export interface NftForwardRule {
|
|
307
|
+
/** nft rule handle, for `nft delete rule … handle N`. */
|
|
308
|
+
handle: number;
|
|
309
|
+
/**
|
|
310
|
+
* The per-identifier value this rule matches on — a concrete source IP or a
|
|
311
|
+
* concrete interface name — or `null` for a rule that is not per-identifier
|
|
312
|
+
* (a host baseline rule matching a whole subnet or a `kici-*` wildcard, or a
|
|
313
|
+
* `jump`).
|
|
314
|
+
*/
|
|
315
|
+
identifier: string | null;
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Read every rule in a table's forward chain, with its handle and the per-agent
|
|
319
|
+
* identifier it matches on.
|
|
320
|
+
*
|
|
321
|
+
* This is the enumerate-what-is-there half of rule management; the
|
|
322
|
+
* identifier-known delete path is {@link parseRuleHandles}. Reconciliation
|
|
323
|
+
* needs this one: rules are removed only on the synchronous teardown paths this
|
|
324
|
+
* process drives, so an orchestrator crash, a `kill -9`, or a VM that dies
|
|
325
|
+
* while the orchestrator is down strands `ip saddr <ip> …` rules in the shared
|
|
326
|
+
* chain forever. The allocator then hands that IP to another tenant, who
|
|
327
|
+
* inherits the dead job's allowlist.
|
|
328
|
+
*
|
|
329
|
+
* Returns an empty list rather than throwing when the chain cannot be read —
|
|
330
|
+
* the caller is a best-effort sweep, and a failed read must not be mistaken
|
|
331
|
+
* for "nothing is there".
|
|
332
|
+
*/
|
|
333
|
+
export declare function listForwardRules(opts?: NftOptions): Promise<NftForwardRule[]>;
|
|
334
|
+
/**
|
|
335
|
+
* Every per-agent identifier currently present in the forward chain, mapped to
|
|
336
|
+
* the handles of the rules that carry it.
|
|
337
|
+
*/
|
|
338
|
+
export declare function listIsolationRules(opts?: NftOptions): Promise<Map<string, number[]>>;
|
|
339
|
+
/**
|
|
340
|
+
* Delete rules from a table's forward chain by handle, highest first so earlier
|
|
341
|
+
* deletions cannot shift the handles still to come.
|
|
342
|
+
*
|
|
343
|
+
* Best-effort per handle: a rule another path already removed must not abort
|
|
344
|
+
* the rest of the sweep.
|
|
345
|
+
*/
|
|
346
|
+
export declare function deleteForwardRules(handles: number[], opts?: NftOptions): Promise<number>;
|
|
347
|
+
/**
|
|
348
|
+
* Parse nft rule listing output and extract handles for rules matching a given identifier.
|
|
349
|
+
* Handles lines like: ` iifname "veth-abc" ip daddr 10.0.0.0/8 drop # handle 42`
|
|
350
|
+
*
|
|
351
|
+
* @param nftOutput - Raw output from `nft -a list chain ip kici <chain>`
|
|
352
|
+
* @param identifier - String to search for in each rule line (interface name or UID)
|
|
353
|
+
* @returns Array of numeric rule handles
|
|
354
|
+
*/
|
|
355
|
+
export declare function parseRuleHandles(nftOutput: string, identifier: string): number[];
|
|
356
|
+
//# sourceMappingURL=nftables.d.ts.map
|