/**
 * Config-bundle export sanitization.
 *
 * Phase 3 of the secrets refactor: every export path (the
 * `/api/config/bundle` GET endpoint and the `studiograph push --remote`
 * CLI) routes through this module so the produced bundles are
 * byte-identical and the registry's `exportable` flag is the single
 * source of truth for "what may leave the trust boundary."
 *
 * Rules driven by the registry:
 *   - bootstrap classification          → never exportable
 *   - any *-secret classification       → never exportable (defense-in-depth;
 *                                         registry already says exportable=false)
 *   - exportable=true entries           → included
 *   - non-registered top-level fields   → included by default (they're the
 *                                         workspace shape: name, slug, repos,
 *                                         etc.) UNLESS they look like a known
 *                                         secret-shape (URL credentials,
 *                                         prefix-matched tokens). Phase 9b
 *                                         tightens this to a strict-allowlist
 *                                         schema; Phase 3 keeps the seam wide
 *                                         enough to ship.
 *
 * H1 (Codex): the sanitizer + the import gate share a SINGLE detector
 * (`isSecretLeak()` below). They walk every object/array to arbitrary
 * depth — the previous top-level-only loop missed nested
 * `r2_config.secret_access_key` and friends. A small static legacy-path
 * map (`LEGACY_SECRET_PATHS`) covers known nested slots that pre-date
 * the registry's flat naming so e.g. `r2_config.secret_access_key` is
 * recognized as the registry's `r2.secret_access_key`.
 *
 * H2 (Codex): connectors get the same treatment with stricter rules —
 * any string value at a secret-named key (`looksLikeSecretFieldName`)
 * MUST be a `$ENV_VAR` placeholder. Raw values are dropped on export
 * and rejected on import.
 */
import { type KeyDef } from './registry.js';
export interface AuthSeedShape {
    jwtSecret?: string;
    apiKey?: string;
    users: Array<Record<string, unknown>>;
    folder_access?: Array<Record<string, unknown>>;
    [k: string]: unknown;
}
export interface ConfigBundleShape {
    workspace: Record<string, unknown> | null;
    about: string | null;
    authSeed: AuthSeedShape;
    sources: Record<string, unknown>;
    connectors: Array<Record<string, unknown>>;
}
/**
 * Heuristic: does this string look like a raw secret value that should
 * never appear in a bundle? Cheap prefix + URL-credential check.
 * Placeholder shape (`$ENV_VAR`) is explicitly NOT a leak.
 */
export declare function looksLikeRawSecret(s: string): boolean;
/**
 * Heuristic: does this field name suggest its value is a secret?
 * Used by the connector detector to require `$ENV_VAR` placeholders
 * for any secret-named key.
 */
export declare function looksLikeSecretFieldName(name: string): boolean;
/**
 * Validate a connector URL doesn't carry embedded credentials.
 */
export declare function urlContainsCredentials(s: string): boolean;
/**
 * The single secret-detector used by both the export sanitizer and the
 * import gate. Returns `true` if this (path, value) pair represents a
 * leak — i.e. the value MUST NOT cross the trust boundary.
 *
 * Asymmetry between export and import would itself be a finding;
 * symmetry here is load-bearing.
 */
export declare function isSecretLeak(path: string, value: unknown): boolean;
/**
 * Walk an object/array tree and return an array of `(path, value)`
 * pairs flagged by `isSecretLeak`. Used by `bundleContainsBootstrapFields`
 * to produce a complete list for error messages.
 *
 * `prefix` is the dotted path so far; the root caller passes `''`.
 */
export declare function findSecretLeaks(prefix: string, value: unknown): Array<{
    path: string;
    reason: 'registry' | 'value-shape' | 'connector';
}>;
/**
 * Drop any field from the seed that the registry classifies as
 * bootstrap. `users[].password_hash` is intentionally retained — see
 * docs/SECURITY.md for the product decision.
 *
 * Walks recursively so nested secret-shaped values inside `users[]` or
 * `folder_access[]` are also stripped.
 */
export declare function sanitizeAuthSeed(seed: AuthSeedShape): AuthSeedShape;
/**
 * Filter a workspace.json-ish object by walking the entire tree and
 * dropping any (path, value) pair flagged by `isSecretLeak`.
 *
 * Phase 3 keeps unknown leaf fields by default; Phase 9b tightens via
 * WorkspaceConfigSchema.
 */
export declare function sanitizeWorkspaceConfig(workspace: Record<string, unknown> | null): Record<string, unknown> | null;
/**
 * Connector configs use placeholder substitution (`$ENV_VAR`) for any
 * secret-bearing field. Walk to arbitrary depth and reject any value
 * at a secret-named key that isn't a placeholder. URLs with embedded
 * credentials are dropped wholesale.
 *
 * Same detector as the import gate — see `connectorImportLeaks`.
 */
export declare function sanitizeConnectorConfigs<T extends Record<string, unknown>>(connectors: T[]): T[];
/**
 * Walk every connector and return the list of (path, key, reason)
 * triples flagged by `isConnectorLeak`. Used by the import gate to
 * reject bundles carrying literal connector secrets.
 */
export declare function connectorImportLeaks(connectors: ReadonlyArray<Record<string, unknown>>): string[];
/**
 * Compose an export-ready bundle from raw workspace materials. Used by
 * both the GET /api/config/bundle endpoint and the `studiograph push
 * --remote` CLI so they produce byte-identical output for the same
 * workspace state.
 */
export declare function buildConfigBundle(input: ConfigBundleShape): ConfigBundleShape;
/**
 * Re-sanitize an inbound bundle (POST /api/config/bundle). Returns the
 * list of paths within the bundle that carried bootstrap-class or
 * secret-class material. Caller rejects the request when the result
 * is non-empty.
 *
 * Walks the auth seed, workspace, and every connector to arbitrary
 * depth using the same detectors as the export sanitizer. Any
 * asymmetry between export and import would itself be a finding.
 */
export declare function bundleContainsBootstrapFields(bundle: ConfigBundleShape): string[];
/**
 * Re-export the registry's `exportable` filter for tests.
 */
export declare function exportableRegistryEntries(): KeyDef[];
