Validate every singleton Domain Store row

This commit is contained in:
ms2k 2026-07-15 14:22:43 +09:00
commit 1fd87ba1bd
4 changed files with 36 additions and 11 deletions

View file

@ -167,13 +167,7 @@ impl HostCoordinatorDomainStore {
pub fn snapshot(&self) -> Result<DomainStore, DomainStoreError> {
self.validate_metadata()?;
let read = self.database.begin_read()?;
let domain = {
let table = read.open_table(DOMAINS)?;
let value = table
.get(SINGLETON_KEY)?
.ok_or(DomainStoreError::MissingRow("Domain"))?;
decode(value.value())?
};
let domain = read_domain(&read.open_table(DOMAINS)?)?;
let generations = read_generations(&read.open_table(GENERATIONS)?)?;
let participants = read_participants(&read.open_table(PARTICIPANTS)?)?;
let sessions = read_sessions(&read.open_table(SESSIONS)?)?;
@ -309,6 +303,21 @@ fn read_generations(
Ok(records)
}
fn read_domain(table: &redb::ReadOnlyTable<u8, &[u8]>) -> Result<Domain, DomainStoreError> {
let mut domain = None;
for entry in table.iter()? {
let (key, value) = entry?;
let record: Domain = decode(value.value())?;
if key.value() != SINGLETON_KEY {
return Err(DomainStoreError::InvalidRow("Domain key"));
}
if domain.replace(record).is_some() {
return Err(DomainStoreError::DuplicateRow("Domain"));
}
}
domain.ok_or(DomainStoreError::MissingRow("Domain"))
}
fn read_participants(
table: &redb::ReadOnlyTable<&[u8; 9], &[u8]>,
) -> Result<Vec<Participant>, DomainStoreError> {

View file

@ -248,6 +248,22 @@ fn metadata_and_typed_row_validation_fail_closed() {
HostCoordinatorDomainStore::open(lock, &row_directory.path("domain.redb"), &domain()),
Err(DomainStoreError::InvalidRow("Generation key"))
));
let domain_directory = TestDirectory::new("extra-domain-row");
drop(open_store(&domain_directory));
let database = Database::open(domain_directory.path("domain.redb")).unwrap();
let write = database.begin_write().unwrap();
{
let mut table = write.open_table(DOMAINS).unwrap();
table.insert(1, &[0xff, 0xff][..]).unwrap();
}
write.commit().unwrap();
drop(database);
let lock = try_exclusive_flock(lock_file(&domain_directory.path("domain.lock"))).unwrap();
assert!(matches!(
HostCoordinatorDomainStore::open(lock, &domain_directory.path("domain.redb"), &domain()),
Err(DomainStoreError::Codec(_))
));
}
#[test]

View file

@ -999,9 +999,9 @@ match its policy digest, fingerprint, namespace, and committed RS identity.
Intent acquisition is monotonic: optional fields move only absent-to-present,
and newly acquired child slots move only `Unassigned` to `Staged` to exact
matching current `Committed`. A current committed owner or supervisor may
enter a newly created dependent-role snapshot directly and is then immutable;
legacy ownership cannot use this rule. Recorded evidence cannot be replaced or
cleared. A transition to ReleaseIntent preserves all evidence; PreparedLower
enter a newly created dependent-role snapshot directly and is then immutable.
Recorded evidence cannot be replaced or cleared. A transition to ReleaseIntent
preserves all evidence; PreparedLower
changes only its lease phase to `ReleaseIntent`. ReleaseIntent becomes Clean
only after independent absence proof and atomically clears every physical
field. A bare Intent may become Clean only after the same sealed runtime-absence

View file

@ -1030,7 +1030,7 @@ not duplicated here.
| **FR-1** | Lazaret shall derive a stable Domain ID from the selected Agent Profile and canonical Agent Workspace. |
| **FR-3** | Tracked and non-ignored workspace edits and supported Git operations shall propagate bidirectionally between host and sandbox. |
| **FR-4** | Selected sandbox paths and Ignored Workspace State shall persist across Domain restarts without copying Lazaret host configuration into the sandbox. |
| **FR-7** | Product recovery shall inspect external runtime resources independently of owner-process liveness. Host-process action shall require the retained pidfd or `PidfsRecoveryMatch` against staged or committed current `PidfsProcessIdentityV1`; legacy PID/start-only ownership, unequal pidfs authority, boot or namespace ambiguity, and unreadable negative evidence shall refuse without numeric-PID signaling. Staged ownership authorizes only exact cleanup/recovery, never service or adoption. Recovery shall also compare typed namespace, mount, artifact, path, and inode evidence before physical cleanup. |
| **FR-7** | Product recovery shall inspect external runtime resources independently of owner-process liveness. Host-process action shall require the retained pidfd or `PidfsRecoveryMatch` against staged or committed current `PidfsProcessIdentityV1`; unequal pidfs authority, boot or namespace ambiguity, and unreadable negative evidence shall refuse without numeric-PID signaling. Staged ownership authorizes only exact cleanup/recovery, never service or adoption. Recovery shall also compare typed namespace, mount, artifact, path, and inode evidence before physical cleanup. |
| **FR-8** | Lazaret shall support interactive terminal sessions without degrading supported host terminal capabilities. |
| **FR-9** | Trusted host configuration shall allow the developer to disable Git-ignored path isolation for an Agent Profile. |
| **FR-11** | The host CLI shall list, inspect, structurally verify, and reset all or selected State Anchors without requiring direct manipulation of Lazaret's storage layout. |