fix(setup): unblock clean Linux installs and add enabled_tables allowlist

- Pin managed Python runtime to 3.13 via `uv venv --python 3.13` so installs
  don't pick the system 3.12 on Ubuntu 24.04 and fail at wheel install.
- Sanitize NO_PROXY/no_proxy for the daemon child process — drop IPv6 CIDR
  entries that httpx rejects with InvalidURL (OrbStack injects these by
  default).
- Add `enabled_tables` allowlist on warehouse connections (zod schema +
  live-database introspection filter) to scope ingest to specific tables.
- Add `getting-started/troubleshooting-linux` docs page covering the Python
  3.13 prerequisite, IPv6 proxy gotcha, and a minimal working recipe; link
  it from the quickstart troubleshooting table and the llms-docs map.
- Make docs-site origin overridable via `KTX_DOCS_ORIGIN` so local builds
  can serve under host.docker.internal.
This commit is contained in:
Andrey Avtomonov 2026-05-19 14:17:52 +02:00
parent c513d61dca
commit 2403f58eff
12 changed files with 307 additions and 8 deletions

View file

@ -216,4 +216,40 @@ describe('createDaemonLiveDatabaseIntrospection', () => {
);
expect(runJson).not.toHaveBeenCalled();
});
it('filters out tables not on the enabled_tables allowlist', async () => {
const runJson = vi.fn(async () => daemonResponse);
const introspection = createDaemonLiveDatabaseIntrospection({
connections: {
warehouse: {
driver: 'postgres',
url: 'postgres://localhost:5432/warehouse',
enabled_tables: ['public.orders'],
},
},
schemas: ['public'],
runJson,
});
const snapshot = await introspection.extractSchema('warehouse');
expect(snapshot.tables.map((table) => `${table.db}.${table.name}`)).toEqual(['public.orders']);
});
it('passes through every table when enabled_tables is omitted or empty', async () => {
const runJson = vi.fn(async () => daemonResponse);
const introspection = createDaemonLiveDatabaseIntrospection({
connections: {
warehouse: {
driver: 'postgres',
url: 'postgres://localhost:5432/warehouse',
enabled_tables: [],
},
},
schemas: ['public'],
runJson,
});
const snapshot = await introspection.extractSchema('warehouse');
expect(snapshot.tables.map((table) => table.name)).toEqual(['customers', 'orders']);
});
});

View file

@ -243,11 +243,29 @@ export function createDaemonLiveDatabaseIntrospection(
const raw = requestJson
? await requestJson('/database/introspect', payload)
: await runJson('database-introspect', payload);
return mapDaemonSnapshot(raw, {
const snapshot = mapDaemonSnapshot(raw, {
connectionId,
extractedAt: now().toISOString(),
schemas,
});
return applyEnabledTablesFilter(snapshot, connection);
},
};
}
function applyEnabledTablesFilter(
snapshot: KtxSchemaSnapshot,
connection: KtxProjectConnectionConfig,
): KtxSchemaSnapshot {
const allowlist = (connection as { enabled_tables?: unknown }).enabled_tables;
if (!Array.isArray(allowlist) || allowlist.length === 0) return snapshot;
const allowed = new Set(allowlist.filter((value): value is string => typeof value === 'string'));
if (allowed.size === 0) return snapshot;
return {
...snapshot,
tables: snapshot.tables.filter((table) => {
const qualified = table.db ? `${table.db}.${table.name}` : table.name;
return allowed.has(qualified);
}),
};
}

View file

@ -27,6 +27,12 @@ function warehouseConnectionSchema<const Driver extends WarehouseDriver>(driver:
.min(1)
.optional()
.describe('Warehouse connection URL or DSN; may contain environment-variable references like env:DATABASE_URL.'),
enabled_tables: z
.array(z.string().min(1))
.optional()
.describe(
'Optional allowlist of fully-qualified table names ("schema.table") to ingest. When set, live-database ingest discards any table whose schema-qualified name is not in this list. Useful for smoke-testing deep ingest on a single table.',
),
})
.describe(
`${driver} warehouse connection. Additional driver-tunable fields (e.g. historicSql, context.queryHistory) are accepted and passed through.`,