Snapshot layout, Drizzle output, exact DDL, and encrypted review artifacts
The private @concourse/rsdb workspace package owns all successful snapshots:
packages/rsdb/
├── src/
│ ├── index.ts
│ └── registry.generated.ts
└── generated/
└── <database>/
├── database.sql
└── drizzle/
├── schema.ts
├── relations.ts
└── <complete Drizzle pull output>The database name is part of the repository path. The implementation must reject names that cannot map to one safe path component.
The registry contains one entry for each successfully merged snapshot. It contains stable, sorted database names and lazy imports for the generated schema and relations.
Application builds compile the registry, schema.ts, and relations.ts through @concourse/rsdb.
The type checker must include all registered generated schemas.
The repository formatter and linter must not rewrite exact files under generated/. The future
change must add a narrow exclusion for this directory. Handwritten package files and the generated
registry must satisfy the normal repository checks.
Unselected database directories remain unchanged. A failed selected database also keeps its prior directory and registry entry. A new database does not enter the registry until its first successful snapshot is merged.
The workflow does not infer deletion when a database is missing from inventory. Snapshot removal and registry removal require a separate reviewed change.
The database step calls the programmatic Drizzle Kit pull() API for one MySQL database. It uses
preserved identifier casing and a database-specific output directory.
The command must not include --init. The command can create local baseline files, but it must
not create a migration ledger in Rackspace.
The step keeps the complete pull output, including:
schema.tsrelations.tsThe candidate contains one canonical manifest. Each entry contains a logical path, byte length, and SHA-256 hash.
Step 0 can identify unstable Drizzle snapshot fields through two unchanged pulls. The comparison normalizes only those recorded fields and the temporary output directory. It compares all other bytes.
If any canonical manifest entry changed, the pull request can replace the complete Drizzle output. Reviewers must treat this output as a fresh imported baseline.
Drizzle does not represent every MySQL database object. The database step also creates one
database.sql snapshot with MySQL-native discovery.
The snapshot contains exact creation statements for:
The workflow discovers objects through MySQL metadata and requests each definition with the
applicable SHOW CREATE statement. It sorts each object type and object name before
serialization.
database.sql is a review archive. It is not a restore script.
The snapshot keeps each returned creation statement unchanged between deterministic comment boundaries. The manifest records the object type, object name, byte range, and hash.
The snapshot records related metadata in comments next to the statement. This metadata includes
the SQL mode and collation. The snapshot preserves DEFINER accounts, AUTO_INCREMENT counters,
and other values that MySQL includes in exact output.
CAUTION: Exact output can expose account names and operational metadata. The secret scan and human review must pass before GitHub publication.
Exact counters can change without a structural schema change. These changes are expected because this plan selects exact output instead of normalized output.
The workflow scans candidate plaintext before storage. It scans the plaintext again before GitHub publication.
The scan blocks:
IDENTIFIED BY statements.A match produces a safe terminal error. The result does not contain the matched text.
The workflow creates one deterministic tar archive for all successful candidates. Tar entries use stable path order, file modes, ownership values, and timestamps.
The workflow encrypts the tar stream with AES-256-GCM. The key ring comes from the production environment. The workflow uses the first key for encryption, a random 12-byte initialization vector, and a 16-byte authentication tag.
The workflow uploads only ciphertext to a private Vercel Blob. The application database stores only the artifact metadata. Plaintext DDL never enters Workflow arguments, Workflow results, Turso, logs, or Blob metadata.
A fresh systemAdmin download route gets the private Blob and decrypts it on the server. The
route writes its audit event before the Blob read and fails closed if it cannot. It authenticates
the ciphertext and streams the tar attachment with these response headers:
Cache-Control: private, no-store
Content-Disposition: attachment
Content-Type: application/x-tar
X-Content-Type-Options: nosniffThe workflow creates a deterministic hook token named rackspace-review:<operationId>. It waits
for that hook or a 24-hour durable sleep. The expiry closes the decision window. It does not
remove the Blob.
The approval and rejection actions first write an immutable review decision and audit event. Then
the actions call resumeHook(). The workflow reads the Turso decision after it resumes. A
repeated action reconciles the existing decision.
Use a conditional update for approval, rejection, and expiry. Exactly one result wins. A late or conflicting decision fails.
An approved, rejected, expired, or failed artifact remains in private Blob storage until manual cleanup. A fresh administrator can download an expired artifact, but cannot approve, reject, or publish it. A new refresh is required after expiry.
Snapshot layout, Drizzle output, exact DDL, and encrypted review artifacts
The private @concourse/rsdb workspace package owns all successful snapshots:
packages/rsdb/
├── src/
│ ├── index.ts
│ └── registry.generated.ts
└── generated/
└── <database>/
├── database.sql
└── drizzle/
├── schema.ts
├── relations.ts
└── <complete Drizzle pull output>The database name is part of the repository path. The implementation must reject names that cannot map to one safe path component.
The registry contains one entry for each successfully merged snapshot. It contains stable, sorted database names and lazy imports for the generated schema and relations.
Application builds compile the registry, schema.ts, and relations.ts through @concourse/rsdb.
The type checker must include all registered generated schemas.
The repository formatter and linter must not rewrite exact files under generated/. The future
change must add a narrow exclusion for this directory. Handwritten package files and the generated
registry must satisfy the normal repository checks.
Unselected database directories remain unchanged. A failed selected database also keeps its prior directory and registry entry. A new database does not enter the registry until its first successful snapshot is merged.
The workflow does not infer deletion when a database is missing from inventory. Snapshot removal and registry removal require a separate reviewed change.
The database step calls the programmatic Drizzle Kit pull() API for one MySQL database. It uses
preserved identifier casing and a database-specific output directory.
The command must not include --init. The command can create local baseline files, but it must
not create a migration ledger in Rackspace.
The step keeps the complete pull output, including:
schema.tsrelations.tsThe candidate contains one canonical manifest. Each entry contains a logical path, byte length, and SHA-256 hash.
Step 0 can identify unstable Drizzle snapshot fields through two unchanged pulls. The comparison normalizes only those recorded fields and the temporary output directory. It compares all other bytes.
If any canonical manifest entry changed, the pull request can replace the complete Drizzle output. Reviewers must treat this output as a fresh imported baseline.
Drizzle does not represent every MySQL database object. The database step also creates one
database.sql snapshot with MySQL-native discovery.
The snapshot contains exact creation statements for:
The workflow discovers objects through MySQL metadata and requests each definition with the
applicable SHOW CREATE statement. It sorts each object type and object name before
serialization.
database.sql is a review archive. It is not a restore script.
The snapshot keeps each returned creation statement unchanged between deterministic comment boundaries. The manifest records the object type, object name, byte range, and hash.
The snapshot records related metadata in comments next to the statement. This metadata includes
the SQL mode and collation. The snapshot preserves DEFINER accounts, AUTO_INCREMENT counters,
and other values that MySQL includes in exact output.
CAUTION: Exact output can expose account names and operational metadata. The secret scan and human review must pass before GitHub publication.
Exact counters can change without a structural schema change. These changes are expected because this plan selects exact output instead of normalized output.
The workflow scans candidate plaintext before storage. It scans the plaintext again before GitHub publication.
The scan blocks:
IDENTIFIED BY statements.A match produces a safe terminal error. The result does not contain the matched text.
The workflow creates one deterministic tar archive for all successful candidates. Tar entries use stable path order, file modes, ownership values, and timestamps.
The workflow encrypts the tar stream with AES-256-GCM. The key ring comes from the production environment. The workflow uses the first key for encryption, a random 12-byte initialization vector, and a 16-byte authentication tag.
The workflow uploads only ciphertext to a private Vercel Blob. The application database stores only the artifact metadata. Plaintext DDL never enters Workflow arguments, Workflow results, Turso, logs, or Blob metadata.
A fresh systemAdmin download route gets the private Blob and decrypts it on the server. The
route writes its audit event before the Blob read and fails closed if it cannot. It authenticates
the ciphertext and streams the tar attachment with these response headers:
Cache-Control: private, no-store
Content-Disposition: attachment
Content-Type: application/x-tar
X-Content-Type-Options: nosniffThe workflow creates a deterministic hook token named rackspace-review:<operationId>. It waits
for that hook or a 24-hour durable sleep. The expiry closes the decision window. It does not
remove the Blob.
The approval and rejection actions first write an immutable review decision and audit event. Then
the actions call resumeHook(). The workflow reads the Turso decision after it resumes. A
repeated action reconciles the existing decision.
Use a conditional update for approval, rejection, and expiry. Exactly one result wins. A late or conflicting decision fails.
An approved, rejected, expired, or failed artifact remains in private Blob storage until manual cleanup. A fresh administrator can download an expired artifact, but cannot approve, reject, or publish it. A new refresh is required after expiry.