Every journal calls assertValidPersistenceId in append. No snapshot store and no durable-state store calls anything: MongoSnapshotStore.save and MongoDurableStateStore.upsert validate nothing, and neither do their Cassandra, relational, SQLite, DynamoDB or object-storage counterparts.
The type guard added in this wave closes the type hole — a non-string id can no longer reach a Mongo filter document as an operator expression — but content is a separate question: length bounds, the character class the journals enforce, and whatever a backend's own key grammar requires. A stream whose id a journal would refuse can still be written to a snapshot store beside it, and the two stores then disagree about what identifies an entity.
This is filed low rather than medium because every caller that reaches these stores through PersistentActor or DurableStateActor has already validated at construction; the exposure is an application holding a store directly, which is the shape the read-side guard was filed for.
The shared validator already exists (src/persistence/storage/PersistenceIdValidator.ts) and now carries both the write-side and the read-side function, so the fix is a call per entry point plus a contract test that every store in the matrix refuses the same ids.
Every journal calls
assertValidPersistenceIdinappend. No snapshot store and no durable-state store calls anything:MongoSnapshotStore.saveandMongoDurableStateStore.upsertvalidate nothing, and neither do their Cassandra, relational, SQLite, DynamoDB or object-storage counterparts.The type guard added in this wave closes the type hole — a non-string id can no longer reach a Mongo filter document as an operator expression — but content is a separate question: length bounds, the character class the journals enforce, and whatever a backend's own key grammar requires. A stream whose id a journal would refuse can still be written to a snapshot store beside it, and the two stores then disagree about what identifies an entity.
This is filed low rather than medium because every caller that reaches these stores through
PersistentActororDurableStateActorhas already validated at construction; the exposure is an application holding a store directly, which is the shape the read-side guard was filed for.The shared validator already exists (
src/persistence/storage/PersistenceIdValidator.ts) and now carries both the write-side and the read-side function, so the fix is a call per entry point plus a contract test that every store in the matrix refuses the same ids.