mirror of
https://github.com/Vateron-Media/XC_VM.git
synced 2026-10-25 19:03:14 +01:00
feat(cluster): record the agent's per-epoch key on each token epoch
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Es8UhyPS8MNjuYTagqfGWi
This commit is contained in:
@@ -187,6 +187,7 @@ CREATE TABLE IF NOT EXISTS `cluster_node_epochs` (
|
||||
`epoch` int(10) unsigned NOT NULL,
|
||||
`record` varbinary(2048) NOT NULL,
|
||||
`token_sealed` varbinary(4096) DEFAULT NULL,
|
||||
`agent_eph_pub` binary(32) DEFAULT NULL,
|
||||
`nbf` int(11) NOT NULL,
|
||||
`exp` int(11) NOT NULL,
|
||||
`refresh_at` int(11) NOT NULL,
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
-- Reverse 035_add_cluster_epoch_eph.sql.
|
||||
ALTER TABLE `cluster_node_epochs` DROP COLUMN IF EXISTS `agent_eph_pub`;
|
||||
@@ -0,0 +1,5 @@
|
||||
-- The agent's per-epoch X25519 key an epoch's token was sealed to. A refresh
|
||||
-- whose reply was lost is retried with the same key, and MAIN re-sends the
|
||||
-- unused next epoch instead of minting another, so a node never holds more
|
||||
-- than two valid epochs.
|
||||
ALTER TABLE `cluster_node_epochs` ADD COLUMN IF NOT EXISTS `agent_eph_pub` binary(32) DEFAULT NULL AFTER `token_sealed`;
|
||||
@@ -9,7 +9,7 @@ use PHPUnit\Framework\TestCase;
|
||||
* where CI has no database.
|
||||
*/
|
||||
final class ClusterSchemaTest extends TestCase {
|
||||
private const MIGRATIONS = ['028_add_cluster_settings', '029_create_cluster_nodes', '030_create_cluster_commands', '031_create_cluster_enrolment', '032_create_cluster_audit', '033_add_crontab_role', '034_create_cluster_changes'];
|
||||
private const MIGRATIONS = ['028_add_cluster_settings', '029_create_cluster_nodes', '030_create_cluster_commands', '031_create_cluster_enrolment', '032_create_cluster_audit', '033_add_crontab_role', '034_create_cluster_changes', '035_add_cluster_epoch_eph'];
|
||||
|
||||
private function src(string $rPath): string {
|
||||
return (string) file_get_contents(dirname(__DIR__, 2) . '/src/' . $rPath);
|
||||
@@ -30,6 +30,25 @@ final class ClusterSchemaTest extends TestCase {
|
||||
|
||||
public function testClusterTablesMatchDatabaseSql(): void {
|
||||
$rInstall = $this->tables($this->src('bin/install/database.sql'));
|
||||
// Columns a later migration ALTERs into a table: present in database.sql,
|
||||
// absent from the migration that created the table.
|
||||
$rAdded = [];
|
||||
foreach (self::MIGRATIONS as $rName) {
|
||||
if (preg_match_all('/ALTER TABLE `([a-z_]+)` ADD COLUMN IF NOT EXISTS `([a-z_]+)`/', $this->src('migrations/database/up/' . $rName . '.sql'), $rM, PREG_SET_ORDER)) {
|
||||
foreach ($rM as [, $rTable, $rColumn]) {
|
||||
$rAdded[$rTable][] = $rColumn;
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach ($rAdded as $rTable => $rColumns) {
|
||||
if (!isset($rInstall[$rTable])) {
|
||||
continue;
|
||||
}
|
||||
foreach ($rColumns as $rColumn) {
|
||||
$this->assertStringContainsString('`' . $rColumn . '`', $rInstall[$rTable], $rTable . '.' . $rColumn);
|
||||
$rInstall[$rTable] = (string) preg_replace('/\n\s*`' . $rColumn . '` [^\n]*/', '', $rInstall[$rTable]);
|
||||
}
|
||||
}
|
||||
$rCount = 0;
|
||||
foreach (self::MIGRATIONS as $rName) {
|
||||
foreach ($this->tables($this->src('migrations/database/up/' . $rName . '.sql')) as $rTable => $rBody) {
|
||||
|
||||
Reference in New Issue
Block a user