feat: bind evidence credentials through local files
This commit is contained in:
@@ -10,11 +10,17 @@ import {
|
||||
type WorkspaceDescriptor,
|
||||
} from "./schema.js";
|
||||
|
||||
export interface ResolvedEvidenceBinding {
|
||||
values: Record<string, string>;
|
||||
missing: string[];
|
||||
}
|
||||
|
||||
export interface RuntimeBindings {
|
||||
dwh: ResolvedBinding;
|
||||
vector: ResolvedBinding;
|
||||
vectorWriter: ResolvedBinding;
|
||||
embedding: ResolvedBinding;
|
||||
evidence: ResolvedEvidenceBinding;
|
||||
}
|
||||
|
||||
export interface ResolvedBinding {
|
||||
@@ -71,7 +77,7 @@ function isSafeSecretFile(path: string, secretRoots: readonly string[]): boolean
|
||||
|
||||
function requiredSuffixes(
|
||||
workspace: WorkspaceDescriptor,
|
||||
role: InstallationRole,
|
||||
role: Exclude<InstallationRole, "EVIDENCE">,
|
||||
transport: DwhTransport | VectorTransport,
|
||||
): readonly InstallationSuffix[] {
|
||||
if (role === "EMBEDDING") return EMBEDDING_REQUIRED_SUFFIXES;
|
||||
@@ -91,7 +97,7 @@ function requiredSuffixes(
|
||||
*/
|
||||
export function resolveBinding(
|
||||
workspace: WorkspaceDescriptor,
|
||||
role: InstallationRole,
|
||||
role: Exclude<InstallationRole, "EVIDENCE">,
|
||||
env: NodeJS.ProcessEnv,
|
||||
secretRoots: readonly string[],
|
||||
): ResolvedBinding {
|
||||
@@ -136,6 +142,39 @@ export function resolveBinding(
|
||||
return { transport: selectedTransport, values, missing };
|
||||
}
|
||||
|
||||
/** Resolve descriptor-selected Evidence credentials without reading any secret file contents. */
|
||||
export function resolveEvidenceBinding(
|
||||
workspace: WorkspaceDescriptor,
|
||||
env: NodeJS.ProcessEnv,
|
||||
secretRoots: readonly string[],
|
||||
): ResolvedEvidenceBinding {
|
||||
const descriptor = validateWorkspaceDescriptor(workspace);
|
||||
const variables = buildInstallationContract(descriptor).variables
|
||||
.filter((variable) => variable.role === "EVIDENCE");
|
||||
if (variables.length === 0) return { values: {}, missing: [] };
|
||||
|
||||
const source = "evidence" in descriptor ? descriptor.evidence?.source : undefined;
|
||||
const required = new Set<InstallationSuffix>(
|
||||
source?.type === "http"
|
||||
? ["SIGNED_URLS_FILE"]
|
||||
: source?.type === "s3"
|
||||
? ["ACCESS_KEY_FILE", "SECRET_KEY_FILE"]
|
||||
: [],
|
||||
);
|
||||
const values: Record<string, string> = {};
|
||||
const missing: string[] = [];
|
||||
for (const variable of variables) {
|
||||
const value = env[variable.name];
|
||||
const present = value !== undefined && value.trim() !== "";
|
||||
const safe = present && isSafeSecretFile(value, secretRoots);
|
||||
if ((required.has(variable.suffix) && !present) || (present && !safe)) {
|
||||
missing.push(variable.name);
|
||||
}
|
||||
if (safe) values[variable.name] = value;
|
||||
}
|
||||
return { values, missing };
|
||||
}
|
||||
|
||||
/** Resolve all runtime roles together so optional writer credentials cannot be smuggled into reader bindings. */
|
||||
export function resolveRuntimeBindings(
|
||||
workspace: WorkspaceDescriptor,
|
||||
@@ -149,6 +188,7 @@ export function resolveRuntimeBindings(
|
||||
vector: resolveBinding(descriptor, "VECTOR", env, secretRoots),
|
||||
vectorWriter: resolveBinding(descriptor, "VECTOR_WRITER", env, secretRoots),
|
||||
embedding: resolveBinding(descriptor, "EMBEDDING", env, secretRoots),
|
||||
evidence: resolveEvidenceBinding(descriptor, env, secretRoots),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -157,5 +197,7 @@ export function resolveRuntimeBindings(
|
||||
* session runtime has no tunnel owner. Keep activation fail-closed until that lifecycle exists.
|
||||
*/
|
||||
export function supportsSessionRuntime(bindings: RuntimeBindings): boolean {
|
||||
return bindings.dwh.transport !== "ssh_tunnel" && bindings.vector.transport !== "ssh_tunnel";
|
||||
return bindings.dwh.transport !== "ssh_tunnel"
|
||||
&& bindings.vector.transport !== "ssh_tunnel"
|
||||
&& (bindings.evidence?.missing.length ?? 0) === 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user