feat(ql3): establish 3.0 incubation baseline

This commit is contained in:
whyour
2026-08-12 00:25:26 +08:00
parent 4bf92dcfeb
commit c699c32461
2817 changed files with 779642 additions and 653 deletions
@@ -0,0 +1,484 @@
import type { ApiCredentialRepository } from '@qinglong/runtime-core/api-credential';
import type { LocalOwnerPepperRepository } from '@qinglong/runtime-core/local-owner-pepper';
import type { PluginPackageQuarantineRepository } from '@qinglong/runtime-core/plugin-package-quarantine';
import type { PluginPackageAutomationPublicationRepository } from '@qinglong/runtime-core/plugin-package-automation-publication';
import type { LocalSecretEnvelopeRepository } from '@qinglong/runtime-core/local-secret';
import type { ProjectPolicyRepository } from '@qinglong/runtime-core/project-policy';
import type { SecurityAuditSink } from '@qinglong/runtime-core/security-audit';
import type { SecurityAuditRecord } from '@qinglong/runtime-core/security-audit';
import type {
SecurityPolicyFence,
SecuritySubject,
} from '@qinglong/runtime-core/security';
import { LocalSqliteApiCredentialRepository } from '../security/apiCredentialRepository';
import {
assertLocalSqliteOptions,
assertLocalSqlitePathBoundary,
LocalSqliteConfigurationError,
openLocalSqliteClient,
type LocalSqliteDatabaseOptions,
type LocalSqliteProfile,
} from '../storage/config';
import { LocalSqliteOperationAuthority } from '../authority/operationAuthority';
import { LocalSqliteOwnerPepperRepository } from '../local-owner/ownerPepperRepository';
import {
EDGE_PLUGIN_PACKAGE_QUARANTINE_ACTIVE_SOURCE_LIMIT,
LocalSqlitePluginPackageQuarantineRepository,
STANDALONE_PLUGIN_PACKAGE_QUARANTINE_ACTIVE_SOURCE_LIMIT,
} from '../plugin-package/pluginPackageQuarantineRepository';
import { LocalSqlitePluginPackageAutomationPublicationRepository } from '../plugin-package/pluginPackageAutomationPublicationRepository';
import {
insertLocalSecurityAudit,
localSecurityAuditFromRow,
sameSecurityAuditSemantic,
} from '../security/securityPersistence';
import { LocalSqliteSecurityAuthorityStore } from '../security/securityAuthorityStore';
import {
auditLocalSqliteReadiness,
type LocalSqliteReadinessEvidence,
} from '../readiness/readiness';
/**
* One short-lived local authority for authenticated package management.
* It deliberately stays behind an explicit subpath and never migrates schema.
*/
export interface LocalSqlitePluginPackageManagementDatabase {
readonly profile: LocalSqliteProfile;
readonly readiness: LocalSqliteReadinessEvidence;
readonly authority: LocalSqliteOperationAuthority;
readonly apiCredentials: ApiCredentialRepository;
readonly ownerPepper: Pick<LocalOwnerPepperRepository, 'resolveKey'>;
readonly pluginPackageQuarantine: PluginPackageQuarantineRepository;
readonly securityAudit: SecurityAuditSink;
confirmUserCredentialFence(
fence: Readonly<LocalSqliteAuthenticatedUserCredentialFence>,
): void;
confirmDefaultProjectOwnerFence(
fence: Readonly<LocalSqliteAuthenticatedUserCredentialFence>,
): void;
close(): Promise<void>;
}
export interface LocalSqliteOptionalFeatureRuntimeDatabase {
readonly profile: LocalSqliteProfile;
readonly readiness: LocalSqliteReadinessEvidence;
readonly authority: LocalSqliteOperationAuthority;
readonly apiCredentials: ApiCredentialRepository;
readonly ownerPepper: Pick<LocalOwnerPepperRepository, 'resolveKey'>;
readonly projectPolicy: ProjectPolicyRepository;
readonly automationPublications: Pick<
PluginPackageAutomationPublicationRepository,
'findCurrent' | 'findByDigest'
>;
readonly localSecrets: LocalSecretEnvelopeRepository;
readonly securityAudit: SecurityAuditSink;
close(): Promise<void>;
}
export interface LocalSqliteAuthenticatedUserCredentialFence {
readonly credentialId: string;
readonly credentialVersion: number;
readonly pepperKeyId: string;
readonly materialDigest: string;
readonly subjectType: 'user';
readonly subjectId: string;
readonly secretDigest: string;
readonly notBeforeAtMs: number;
readonly expiresAtMs: number;
}
export class LocalSqliteAuthenticatedManagementFenceError extends Error {
readonly code = 'LOCAL_SQLITE_AUTHENTICATED_MANAGEMENT_FENCE_REJECTED';
constructor() {
super('The authenticated local management credential fence was rejected');
this.name = 'LocalSqliteAuthenticatedManagementFenceError';
}
}
export class LocalSqliteAuthenticatedManagementOwnerError extends Error {
readonly code = 'LOCAL_SQLITE_AUTHENTICATED_MANAGEMENT_OWNER_REJECTED';
constructor() {
super('The authenticated local management User is not a current Owner');
this.name = 'LocalSqliteAuthenticatedManagementOwnerError';
}
}
function exactFence(
value: Readonly<LocalSqliteAuthenticatedUserCredentialFence>,
): void {
if (
!value ||
typeof value !== 'object' ||
Array.isArray(value) ||
Object.keys(value).sort().join('\0') !==
[
'credentialId',
'credentialVersion',
'expiresAtMs',
'materialDigest',
'notBeforeAtMs',
'pepperKeyId',
'secretDigest',
'subjectId',
'subjectType',
]
.sort()
.join('\0') ||
typeof value.credentialId !== 'string' ||
value.credentialId.length < 1 ||
value.credentialId.length > 128 ||
!Number.isSafeInteger(value.credentialVersion) ||
value.credentialVersion < 1 ||
typeof value.pepperKeyId !== 'string' ||
value.pepperKeyId.length < 1 ||
value.pepperKeyId.length > 128 ||
!/^[0-9a-f]{64}$/.test(value.materialDigest) ||
value.subjectType !== 'user' ||
typeof value.subjectId !== 'string' ||
value.subjectId.length < 1 ||
value.subjectId.length > 255 ||
!/^[0-9a-f]{64}$/.test(value.secretDigest) ||
!Number.isSafeInteger(value.notBeforeAtMs) ||
value.notBeforeAtMs < 0 ||
!Number.isSafeInteger(value.expiresAtMs) ||
value.expiresAtMs <= value.notBeforeAtMs
) {
throw new LocalSqliteAuthenticatedManagementFenceError();
}
}
function confirmUserCredentialFence(
authority: LocalSqliteOperationAuthority,
fence: Readonly<LocalSqliteAuthenticatedUserCredentialFence>,
): void {
exactFence(fence);
try {
const row = authority.client
.prepare(
`SELECT
credential."state" AS "credentialState",
credential."subject_type" AS "subjectType",
credential."subject_id" AS "subjectId",
credential."secret_digest" AS "secretDigest",
credential."not_before_at_ms" AS "notBeforeAtMs",
credential."expires_at_ms" AS "expiresAtMs",
identity."status" AS "subjectStatus",
binding."pepper_key_id" AS "pepperKeyId",
pepper."state" AS "pepperState",
pepper."material_digest" AS "materialDigest",
CAST(unixepoch('subsec') * 1000 AS INTEGER) AS "nowMs"
FROM "QingLong3ApiCredentials" AS credential
JOIN "QingLong3IdentitySubjects" AS identity
ON identity."subject_type" = credential."subject_type"
AND identity."subject_id" = credential."subject_id"
JOIN "QingLong3ApiCredentialPepperBindings" AS binding
ON binding."credential_id" = credential."credential_id"
AND binding."credential_version" = credential."version"
JOIN "QingLong3LocalOwnerPepperKeys" AS pepper
ON pepper."pepper_key_id" = binding."pepper_key_id"
WHERE credential."credential_id" = ?
AND credential."version" = ?`,
)
.get(fence.credentialId, fence.credentialVersion) as
| Record<string, unknown>
| undefined;
if (
!row ||
row.credentialState !== 'active' ||
row.subjectStatus !== 'active' ||
row.subjectType !== fence.subjectType ||
row.subjectId !== fence.subjectId ||
row.secretDigest !== fence.secretDigest ||
row.pepperKeyId !== fence.pepperKeyId ||
(row.pepperState !== 'active' && row.pepperState !== 'retired') ||
row.materialDigest !== fence.materialDigest ||
row.notBeforeAtMs !== fence.notBeforeAtMs ||
row.expiresAtMs !== fence.expiresAtMs ||
!Number.isSafeInteger(row.nowMs) ||
(row.nowMs as number) < fence.notBeforeAtMs ||
(row.nowMs as number) >= fence.expiresAtMs
) {
throw new LocalSqliteAuthenticatedManagementFenceError();
}
} catch (error) {
if (error instanceof LocalSqliteAuthenticatedManagementFenceError) {
throw error;
}
throw new LocalSqliteAuthenticatedManagementFenceError();
}
}
export function confirmLocalSqliteAuthenticatedUserCredentialFence(
authority: LocalSqliteOperationAuthority,
fence: Readonly<LocalSqliteAuthenticatedUserCredentialFence>,
): void {
confirmUserCredentialFence(authority, fence);
}
export function confirmLocalSqliteProjectPolicyFence(
authority: LocalSqliteOperationAuthority,
projectId: string,
actor: Readonly<SecuritySubject>,
fence: Readonly<SecurityPolicyFence>,
): void {
try {
if (
!Number.isSafeInteger(fence.projectVersion) ||
fence.projectVersion < 1 ||
!Number.isSafeInteger(fence.bindingVersion) ||
(fence.bindingVersion as number) < 1
) {
throw new LocalSqliteAuthenticatedManagementFenceError();
}
const row = authority.client
.prepare(
`SELECT project."status" AS "projectStatus",
project."version" AS "projectVersion",
binding."state" AS "bindingState",
binding."version" AS "bindingVersion"
FROM "QingLong3Projects" AS project
JOIN "QingLong3ProjectRoleBindings" AS binding
ON binding."project_id" = project."id"
AND binding."subject_type" = ?
AND binding."subject_id" = ?
WHERE project."id" = ?
AND binding."version" = (
SELECT max(latest."version")
FROM "QingLong3ProjectRoleBindings" AS latest
WHERE latest."project_id" = binding."project_id"
AND latest."subject_type" = binding."subject_type"
AND latest."subject_id" = binding."subject_id"
)`,
)
.get(actor.type, actor.id, projectId) as
| Record<string, unknown>
| undefined;
if (
!row ||
row.projectStatus !== 'active' ||
row.bindingState !== 'active' ||
row.projectVersion !== fence.projectVersion ||
row.bindingVersion !== fence.bindingVersion
) {
throw new LocalSqliteAuthenticatedManagementFenceError();
}
} catch (error) {
if (error instanceof LocalSqliteAuthenticatedManagementFenceError) {
throw error;
}
throw new LocalSqliteAuthenticatedManagementFenceError();
}
}
const AUTHENTICATED_AUDIT_SELECT = `
"event_id" AS "eventId",
"request_id" AS "requestId",
"operation_id" AS "operationId",
"project_id" AS "auditProjectId",
"subject_type" AS "subjectType",
"subject_id" AS "subjectId",
"authentication_id" AS "authenticationId",
"outcome" AS "outcome",
"reasons_json" AS "reasonsJson",
"fence_project_version" AS "fenceProjectVersion",
"fence_binding_version" AS "fenceBindingVersion",
"occurred_at_ms" AS "occurredAtMs"`;
export function commitLocalSqliteSecurityAuditInTransaction(
authority: LocalSqliteOperationAuthority,
audit: Readonly<SecurityAuditRecord>,
replay: boolean,
): void {
try {
if (!authority.client.isTransaction) {
throw new LocalSqliteAuthenticatedManagementFenceError();
}
const row = authority.client
.prepare(
`SELECT ${AUTHENTICATED_AUDIT_SELECT}
FROM "QingLong3SecurityAuditEvents" WHERE "event_id" = ?`,
)
.get(audit.eventId) as Record<string, unknown> | undefined;
if (replay) {
const stored = row ? localSecurityAuditFromRow(row) : null;
const storedWithoutTime = stored
? Object.freeze({ ...stored, occurredAtMs: audit.occurredAtMs })
: null;
if (
!storedWithoutTime ||
!sameSecurityAuditSemantic(storedWithoutTime, audit)
) {
throw new LocalSqliteAuthenticatedManagementFenceError();
}
return;
}
if (row) throw new LocalSqliteAuthenticatedManagementFenceError();
insertLocalSecurityAudit(authority.client, audit);
} catch (error) {
if (error instanceof LocalSqliteAuthenticatedManagementFenceError) {
throw error;
}
throw new LocalSqliteAuthenticatedManagementFenceError();
}
}
function confirmDefaultProjectOwnerFence(
authority: LocalSqliteOperationAuthority,
fence: Readonly<LocalSqliteAuthenticatedUserCredentialFence>,
): void {
exactFence(fence);
try {
const row = authority.client
.prepare(
`SELECT
project."status" AS "projectStatus",
binding."state" AS "bindingState",
binding."role" AS "role"
FROM "QingLong3Projects" AS project
JOIN "QingLong3ProjectRoleBindings" AS binding
ON binding."project_id" = project."id"
AND binding."subject_type" = ?
AND binding."subject_id" = ?
WHERE project."id" = 'default'
AND binding."version" = (
SELECT max(latest."version")
FROM "QingLong3ProjectRoleBindings" AS latest
WHERE latest."project_id" = binding."project_id"
AND latest."subject_type" = binding."subject_type"
AND latest."subject_id" = binding."subject_id"
)`,
)
.get(fence.subjectType, fence.subjectId) as
| Record<string, unknown>
| undefined;
if (
!row ||
row.projectStatus !== 'active' ||
row.bindingState !== 'active' ||
row.role !== 'owner'
) {
throw new LocalSqliteAuthenticatedManagementOwnerError();
}
} catch (error) {
if (error instanceof LocalSqliteAuthenticatedManagementOwnerError) {
throw error;
}
throw new LocalSqliteAuthenticatedManagementOwnerError();
}
}
export async function openLocalSqlitePluginPackageManagementDatabase(
options: LocalSqliteDatabaseOptions,
): Promise<LocalSqlitePluginPackageManagementDatabase> {
assertLocalSqliteOptions(options);
assertLocalSqlitePathBoundary(options.databasePath, false);
const client = openLocalSqliteClient(options, false);
try {
const readiness = await auditLocalSqliteReadiness(client);
const authority = new LocalSqliteOperationAuthority(client);
const apiCredentials = new LocalSqliteApiCredentialRepository(authority);
const ownerPepper = new LocalSqliteOwnerPepperRepository(authority);
const pluginPackageQuarantine =
new LocalSqlitePluginPackageQuarantineRepository(authority, {
activeSourceLimit:
options.profile === 'edge'
? EDGE_PLUGIN_PACKAGE_QUARANTINE_ACTIVE_SOURCE_LIMIT
: STANDALONE_PLUGIN_PACKAGE_QUARANTINE_ACTIVE_SOURCE_LIMIT,
});
const securityAudit = new LocalSqliteSecurityAuthorityStore(authority);
let closePromise: Promise<void> | undefined;
return Object.freeze({
profile: options.profile,
readiness,
authority,
apiCredentials,
ownerPepper,
pluginPackageQuarantine,
securityAudit,
confirmUserCredentialFence(
fence: Readonly<LocalSqliteAuthenticatedUserCredentialFence>,
) {
confirmUserCredentialFence(authority, fence);
},
confirmDefaultProjectOwnerFence(
fence: Readonly<LocalSqliteAuthenticatedUserCredentialFence>,
) {
confirmDefaultProjectOwnerFence(authority, fence);
},
close() {
if (closePromise) return closePromise;
closePromise = authority.close();
return closePromise;
},
});
} catch (error) {
if (client.isOpen) client.close();
throw error;
}
}
/**
* Opens one explicit optional-feature runtime authority without loading any
* feature implementation. Callers must inspect their durable feature head
* before importing the optional package and must close this authority on every
* inactive, failed, or stopped path.
*/
export async function openLocalSqliteOptionalFeatureRuntimeDatabase(
options: LocalSqliteDatabaseOptions,
): Promise<LocalSqliteOptionalFeatureRuntimeDatabase> {
assertLocalSqliteOptions(options);
assertLocalSqlitePathBoundary(options.databasePath, false);
const client = openLocalSqliteClient(options, false);
try {
const readiness = await auditLocalSqliteReadiness(client);
const authority = new LocalSqliteOperationAuthority(client);
const securityAuthority = new LocalSqliteSecurityAuthorityStore(authority);
const projectPolicy: ProjectPolicyRepository = Object.freeze({
resolve: (
...[projectId, subject]: Parameters<ProjectPolicyRepository['resolve']>
) => securityAuthority.resolve(projectId, subject),
append: (...[command]: Parameters<ProjectPolicyRepository['append']>) =>
securityAuthority.append(command),
});
let closePromise: Promise<void> | undefined;
return Object.freeze({
profile: options.profile,
readiness,
authority,
apiCredentials: new LocalSqliteApiCredentialRepository(authority),
ownerPepper: new LocalSqliteOwnerPepperRepository(authority),
projectPolicy,
automationPublications:
new LocalSqlitePluginPackageAutomationPublicationRepository(authority),
localSecrets: securityAuthority,
securityAudit: securityAuthority,
close() {
if (closePromise) return closePromise;
closePromise = authority.close();
return closePromise;
},
});
} catch (error) {
if (client.isOpen) client.close();
throw error;
}
}
/**
* Neutral names for shared short-lived authenticated management ceremonies.
* The historical Plugin Package names remain compatible aliases.
*/
export type LocalSqliteAuthenticatedManagementDatabase =
LocalSqlitePluginPackageManagementDatabase;
export const openLocalSqliteAuthenticatedManagementDatabase =
openLocalSqlitePluginPackageManagementDatabase;
export {
LocalSqliteConfigurationError,
type LocalSqliteDatabaseOptions,
type LocalSqliteProfile,
};
export type { LocalSqliteReadinessEvidence } from '../readiness/readiness';
@@ -0,0 +1,126 @@
import type { ApiCredentialRepository } from '@qinglong/runtime-core/api-credential';
import type { LocalOwnerPepperRepository } from '@qinglong/runtime-core/local-owner-pepper';
import type { LocalSecretAdministrationRepository } from '@qinglong/runtime-core/local-secret-administration';
import type { ProjectPolicyRepository } from '@qinglong/runtime-core/project-policy';
import type { SecurityAuditSink } from '@qinglong/runtime-core/security-audit';
import { LocalSqliteApiCredentialRepository } from '../security/apiCredentialRepository';
import {
assertLocalSqliteOptions,
assertLocalSqlitePathBoundary,
LocalSqliteConfigurationError,
openLocalSqliteClient,
type LocalSqliteDatabaseOptions,
type LocalSqliteProfile,
} from '../storage/config';
import { LocalSqliteOperationAuthority } from '../authority/operationAuthority';
import { LocalSqliteOwnerPepperRepository } from '../local-owner/ownerPepperRepository';
import {
confirmLocalSqliteAuthenticatedUserCredentialFence,
LocalSqliteAuthenticatedManagementFenceError,
type LocalSqliteAuthenticatedUserCredentialFence,
} from './packageManagement';
import {
auditLocalSqliteReadiness,
type LocalSqliteReadinessEvidence,
} from '../readiness/readiness';
import { LocalSqliteSecurityAuthorityStore } from '../security/securityAuthorityStore';
/**
* One short-lived authority for authenticated Secret creation and rotation.
* It deliberately excludes task execution, scheduling, plugin management and
* schema migration capabilities.
*/
export interface LocalSqliteSecretAdministrationDatabase {
readonly profile: LocalSqliteProfile;
readonly readiness: LocalSqliteReadinessEvidence;
readonly apiCredentials: ApiCredentialRepository;
readonly ownerPepper: Pick<LocalOwnerPepperRepository, 'resolveKey'>;
readonly projectPolicy: ProjectPolicyRepository;
readonly localSecretAdministration: LocalSecretAdministrationRepository;
readonly securityAudit: SecurityAuditSink;
activateUserCredentialFence(
fence: Readonly<LocalSqliteAuthenticatedUserCredentialFence>,
): void;
close(): Promise<void>;
}
function sameFence(
left: Readonly<LocalSqliteAuthenticatedUserCredentialFence>,
right: Readonly<LocalSqliteAuthenticatedUserCredentialFence>,
): boolean {
return (
left.credentialId === right.credentialId &&
left.credentialVersion === right.credentialVersion &&
left.pepperKeyId === right.pepperKeyId &&
left.materialDigest === right.materialDigest &&
left.subjectType === right.subjectType &&
left.subjectId === right.subjectId &&
left.secretDigest === right.secretDigest &&
left.notBeforeAtMs === right.notBeforeAtMs &&
left.expiresAtMs === right.expiresAtMs
);
}
export async function openLocalSqliteSecretAdministrationDatabase(
options: LocalSqliteDatabaseOptions,
): Promise<LocalSqliteSecretAdministrationDatabase> {
assertLocalSqliteOptions(options);
assertLocalSqlitePathBoundary(options.databasePath, false);
const client = openLocalSqliteClient(options, false);
try {
const readiness = await auditLocalSqliteReadiness(client);
const authority = new LocalSqliteOperationAuthority(client);
let activeFence:
| Readonly<LocalSqliteAuthenticatedUserCredentialFence>
| undefined;
const securityAuthority = new LocalSqliteSecurityAuthorityStore(authority, {
beforeAuthorizedLocalSecretMutation() {
if (!activeFence) {
throw new LocalSqliteAuthenticatedManagementFenceError();
}
confirmLocalSqliteAuthenticatedUserCredentialFence(
authority,
activeFence,
);
},
});
const apiCredentials = new LocalSqliteApiCredentialRepository(authority);
const ownerPepper = new LocalSqliteOwnerPepperRepository(authority);
const projectPolicy: ProjectPolicyRepository = Object.freeze({
resolve: (
...[projectId, subject]: Parameters<ProjectPolicyRepository['resolve']>
) => securityAuthority.resolve(projectId, subject),
append: (...[command]: Parameters<ProjectPolicyRepository['append']>) =>
securityAuthority.append(command),
});
let closePromise: Promise<void> | undefined;
return Object.freeze({
profile: options.profile,
readiness,
apiCredentials,
ownerPepper,
projectPolicy,
localSecretAdministration: securityAuthority,
securityAudit: securityAuthority,
activateUserCredentialFence(
fence: Readonly<LocalSqliteAuthenticatedUserCredentialFence>,
) {
confirmLocalSqliteAuthenticatedUserCredentialFence(authority, fence);
if (activeFence && !sameFence(activeFence, fence)) {
throw new LocalSqliteAuthenticatedManagementFenceError();
}
activeFence = Object.freeze({ ...fence });
},
close() {
if (closePromise) return closePromise;
closePromise = authority.close();
return closePromise;
},
});
} catch (error) {
if (client.isOpen) client.close();
if (error instanceof LocalSqliteConfigurationError) throw error;
throw error;
}
}