agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
95 lines (94 loc) • 4.75 kB
TypeScript
/**
* directory/ldapDirectory — the {@link Directory} port over LDAPS, through
* `ldapts` (an optional peer, loaded lazily — the `jose` precedent).
*
* LDAPS only: `ldap://` would send the password in the clear on the bind, and
* StartTLS needs the same DC certificate LDAPS does, so refusing it loses
* nothing real. The certificate chain is checked against EITHER the CA the
* operator supplies (`caPem`) OR — said in so many words, `systemCa: true` —
* Node's default CA store, and the host name against the URL. There is no
* switch to skip either check, and no default between the two trusts.
*
* ── What to trust: the ROOT ─────────────────────────────────────────────────
* Trust the CA at the top of the DC certificate's chain, never the DC's own
* certificate (refused at construction: it is not a CA) or an intermediate.
* A DC certificate is renewed every few months, and an intermediate is
* replaced on its own schedule; a root lives for years. Trusting the root
* means a renewal — a new leaf, even a new intermediate the DC sends in its
* chain — needs no change here. A DC certificate issued by a PUBLIC CA chains
* to a root Node already ships (`systemCa: true`); a company CA does not, so
* give its root as `caPem`.
*/
import type { Directory } from './port.js';
/** The slice of `ldapts` this adapter uses, declared structurally. */
export interface LdaptsBackend {
Client: new (options: Record<string, unknown>) => {
bind(name: string, password?: string): Promise<void>;
exop(oid: string): Promise<{
oid?: string;
value?: string;
}>;
search(base: string, options: Record<string, unknown>): Promise<{
searchEntries: Record<string, unknown>[];
}>;
unbind(): Promise<void>;
};
}
export interface LdapDirectoryOptions {
/** `ldaps://dc1.corp.example:636`. `ldap://` is refused. */
readonly url: string;
/**
* The CA certificate(s), PEM, the DC's certificate must chain to — the
* company's ROOT CA. Exactly one of this and `systemCa`.
*/
readonly caPem?: string;
/**
* `true`: trust Node's default CA store instead of a file — the public
* roots Node ships (Mozilla's list), plus `NODE_EXTRA_CA_CERTS`, plus the
* operating system's store when Node runs with `--use-system-ca`. For a DC
* whose certificate a PUBLIC CA issued. Exactly one of this and `caPem`;
* the host-name check stays on either way.
*/
readonly systemCa?: true;
/** Connect and operation timeout, ms. Default 5000. */
readonly timeoutMs?: number;
/** An already-imported `ldapts`. */
readonly backend?: LdaptsBackend;
/**
* Told why the directory could not answer — never reached (with the
* connection or TLS error's CODE, e.g. `UNABLE_TO_VERIFY_LEAF_SIGNATURE`
* for a CA that does not issue the DC's certificate,
* `ERR_TLS_CERT_ALTNAME_INVALID` for a name it does not carry), or failed
* after the bind was sent. For the operator; never the password, the name
* or an error's text. A REFUSED bind is not logged here: its AD sub-code
* travels in the bind's answer to the sign-in door's audit record.
*/
readonly log?: (line: string) => void;
}
/** Raised when `directory-password` is configured and `ldapts` is not installed. */
export declare class MissingLdaptsError extends Error {
readonly code: "ERR_MISSING_LDAPTS";
constructor();
}
export declare function ldapDirectory(options: LdapDirectoryOptions): Directory;
/**
* Every certificate block in a PEM bundle, in order — what
* `pem.match(/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/g)`
* finds, in one pass. The regex re-reads the rest of the bundle from every
* BEGIN line that has no END after it, which is quadratic on a file of
* repeated BEGIN lines; once a BEGIN has no END after it, no later one has
* either, so the scan stops there.
*/
export declare function pemCertificateBlocks(pem: string): string[];
/**
* Why a CA file is not one, or `undefined`: every PEM block must parse as an
* X.509 certificate with the CA flag set (review idI57 N-10 — a leaf
* certificate booted, then failed every sign-in).
*/
export declare function caPemProblem(caPem: unknown): string | undefined;
/**
* The subjects of the CA certificates in a PEM bundle that are NOT
* self-signed roots — intermediates. Trusting one alone breaks at the next
* renewal that changes it, so boot says so (`directory-password`'s banner).
*/
export declare function intermediateCaSubjects(caPem: string): string[];