import { type ServerConfig } from "./api/server.js"; import { VERSION } from "./brand.js"; import type { LoginCredential } from "./api/identity.js"; import type { ApiAuth } from "./api/session.js"; import { type ActionDeclaration, type Route } from "./api/routes.js"; import type { FixProposal, OwnershipModel, SchemaSnapshot, ProbeResult, RunReport, SeedResult, TableOwnership } from "./types.js"; export { VERSION }; export interface RunOptions { connectionString: string; schemas?: string[]; mode?: "read_only" | "full"; /** Developer corrections to the inferred model. */ overrides?: Record>; /** Explicit roles, for what role inference cannot settle. */ roles?: { anonymous?: string; authenticated?: string; }; /** Explicit user table, for when structural inference names the wrong one. */ userTable?: string; /** * Private tables the developer has explicitly accepted as uncheckable. * An unchecked table holding user data otherwise makes the run inconclusive — * the gap stays visible in the coverage list either way, but naming it here * says a human has seen it and signed off. */ acceptUnchecked?: string[]; /** Reuse an already-computed model instead of inferring one. */ model?: OwnershipModel; /** * Also probe a running application. Requires an ephemeral environment: these * requests write for real and cannot be rolled back. * * Every field but `cwd` is optional, and that is the point. With none of them * set, Crossline works out whether the API plane is the only plane that could * settle anything, starts the application itself if so, and detects how to be * a logged-in user from the project's own dependencies — which is what makes * this a default rather than six steps. */ api?: { /** Where the app is. Omitted means "start it and find out". */ baseUrl?: string; /** How to be a logged-in user. Omitted, or `none`, means "detect it". */ auth?: ApiAuth; cwd: string; /** Endpoints named in the config file, merged with what discovery finds. */ routes?: Route[]; /** * Ambient environment, for detecting the auth provider. Defaults to * `process.env`, which is what the CLI wants. Passed explicitly by a caller * that must not be able to pick up a credential from the machine it happens * to be running on. */ env?: NodeJS.ProcessEnv; /** * How to call the Next.js server actions the build named, joined to the ids * discovery read out of it. Without these an action is named and not called. */ actions?: ActionDeclaration[]; /** * How to start the application. `false` refuses to start anything, for a * repository where running a command would be wrong. */ server?: ServerConfig | false; /** * This block is an *offer*, not a request. * * The CLI passes one on every run so that a project which needs the API * plane gets it without anybody typing `--api`. That must not turn every * ordinary Supabase run into "`--api` was given and checked nothing", so an * offered block is taken up only where the data plane could not have * settled the question by itself and the project is recognisably a web * application. Declined, it leaves no trace: the run is exactly the * data-plane run it would have been. */ auto?: boolean; }; } export interface RunOutcome { report: RunReport; snapshot: SchemaSnapshot; seedResult: SeedResult; probes: ProbeResult[]; brokenOwnerAccess: ProbeResult[]; } /** * One full pass: read the schema, work out who owns what, plant known data for * two unrelated users, and try to cross every line between them. * * Everything happens inside a single transaction that is rolled back at the * end, so a run leaves no rows, no sequence drift, and no evidence behind in * the target database — even the writes we attempt as part of the test. */ export declare function run(opts: RunOptions): Promise; export interface HttpRunOptions { /** Where the application is. Omitted means "start it and find out". */ baseUrl?: string; /** The project, for route discovery and for the start command. */ cwd: string; mode?: "read_only" | "full"; /** Endpoints named in the config file, merged with what discovery finds. */ routes?: Route[]; /** How to start the application. `false` refuses to start anything. */ server?: ServerConfig | false; /** * Ownership the developer or the agent states, keyed by collection path: * `{"/api/notes": "ownerId"}`. * * The rank below self-attestation, and the answer for an application whose * accounts are numbered rather than random — there the run cannot tell an * owner field from a counter, and somebody who knows the application can. * Refused where the run can see the declaration is wrong. */ declaredOwners?: Record; /** * Two existing accounts to log in as, instead of signing two up. For an * application with registration closed, and for an agent that already has * test credentials. */ accounts?: [LoginCredential, LoginCredential]; } export interface HttpRunOutcome { report: RunReport; } /** * One full pass against an application whose data Crossline cannot reach. * * A Rails app on MySQL, a Django app on Mongo, a Go service on DynamoDB, or a * Next.js app whose state lives in a service nobody here has a driver for. There * is no schema to read, no ownership to infer, no role to impersonate and no * transaction to roll back — so this shares no step with {@link run} beyond the * shape of the report it produces, and it is a separate function for that * reason rather than a mode of the same one. Nothing on this path can change * what a database-backed run does. * * What it *is* entitled to say is narrower than the database-backed run and * exactly stated: two accounts were created through the application's own * signup, each confirmed by the application naming it back; each created a * resource carrying 128 bits nothing else on earth has; and then each was asked * for the other's. A write or a delete that lands is a finding. A read that * comes back is a finding only where the run established who the resource * belongs to, and an observation everywhere else, because no sharing model was * ever read. */ export declare function runOverHttp(opts: HttpRunOptions): Promise; export interface FixOutcome { outcome: RunOutcome; fixes: FixProposal[]; allVerified: boolean; applyError: string | null; } /** * Find the holes, write the policies that close them, apply those policies, and * run the whole suite again to prove they worked without breaking anything. * * Detect -> fix -> re-verify is the loop that makes this a product rather than * a source of anxiety, so nothing skips the last step. */ export declare function fix(opts: RunOptions): Promise; export { redact } from "./db/connect.js";