Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
06d97b3
feat(infra): create the Rust workspace skeleton
forkwright Sep 25, 2026
4946de0
chore(infra): add the supply-chain policy files
forkwright Sep 25, 2026
e9e58cf
ci: run the cargo gate and move doc checks to docs.yml
forkwright Sep 25, 2026
be2b400
ci: add security scans, CodeQL for Rust, and cargo updates
forkwright Sep 25, 2026
711d122
chore(infra): bump Cargo versions in release PRs
forkwright Sep 25, 2026
375313d
docs: record the Phase 01 implementation kickoff
forkwright Sep 25, 2026
9403a08
Merge remote-tracking branch 'origin/main' into feat/workspace-bootstrap
forkwright Sep 25, 2026
aaa962b
ci: keep gate checks on docs-only PRs until docs is required
forkwright Sep 25, 2026
3b6a34c
chore(infra): tighten license allowances and kickoff wording
forkwright Sep 25, 2026
9417707
feat(phylake): add root key file and at-rest sealing primitives
forkwright Sep 25, 2026
d2f7d73
Merge remote-tracking branch 'origin/main' into pr6-review
forkwright Sep 25, 2026
441774e
chore(infra): add rkyv, snafu, and toml for the contract crate
forkwright Sep 25, 2026
df59b87
feat(syntheke): implement the Phase 01 capability contract types
forkwright Sep 25, 2026
e023396
test(syntheke): cover wire round-trips and contract fixtures
forkwright Sep 25, 2026
021f0ab
fix(phylake): refuse symlinked, special and foreign-owned key files
forkwright Sep 25, 2026
6953a85
test(phylake): pin sealed and wrapped formats to known answers
forkwright Sep 25, 2026
d0ca55b
Merge remote-tracking branch 'origin/main' into pr6-review
forkwright Sep 25, 2026
e5c4dad
feat(syntheke): designate the authorizing grant on every request
forkwright Sep 25, 2026
3b77633
docs(design): make grant designation explicit in contract v1
forkwright Sep 25, 2026
4cf5438
chore(infra): record why the syn 2 skip stays and when it retires
forkwright Sep 25, 2026
4bb6b7e
refactor(phylake): move test modules into tests.rs files
forkwright Sep 25, 2026
c67190c
feat(epitrope): implement grant narrowing, validity, budgets, lifecycle
forkwright Sep 25, 2026
9d2322f
Merge remote-tracking branch 'origin/main' into feat/epitrope-authz
forkwright Sep 25, 2026
e08394f
ci: scope CodeQL security queries to production code
forkwright Sep 25, 2026
39d7118
Merge remote-tracking branch 'origin/feat/phylake-crypto' into feat/p…
forkwright Sep 25, 2026
ff9ae99
feat(syntheke): add BudgetUnavailable and the narrowing axis on Denied
forkwright Sep 25, 2026
eb10cb4
chore(phylake): declare fjall, epitrope, rkyv, and syntheke dependencies
forkwright Sep 25, 2026
aa2ac8d
Merge remote-tracking branch 'origin/feat/phylake-crypto' into feat/p…
forkwright Sep 25, 2026
316af50
fix(epitrope): map upstream exhaustion, name the axis, harden the walk
forkwright Sep 25, 2026
b27151e
Merge remote-tracking branch 'origin/main' into pr6-merge
forkwright Sep 25, 2026
b1bcf17
docs(design): record the authorization resolutions in contract v1
forkwright Sep 25, 2026
1965788
test(syntheke): move the vocab, budget, and ids tests to sibling files
forkwright Sep 25, 2026
0ad5473
feat(syntheke): add the SessionRequired deny code
forkwright Sep 25, 2026
e7bd3c6
feat(epitrope): enforce each capability's session requirement
forkwright Sep 25, 2026
26615b1
feat(epitrope): authorize GrantRevoke only within the designated subtree
forkwright Sep 25, 2026
7d27686
docs(design): state the revoke rule and session requirement in contra…
forkwright Sep 25, 2026
802e8b3
refactor(phylake): draw entropy into uninitialized buffers
forkwright Sep 25, 2026
f9f0e81
Merge remote-tracking branch 'origin/main' into feat/epitrope-authz
forkwright Sep 25, 2026
d6d3fdb
feat(phylake): add the custody store with B1-B5 transactions and reco…
forkwright Sep 25, 2026
5fe89e3
Merge remote-tracking branch 'origin/feat/phylake-crypto' into feat/p…
forkwright Sep 25, 2026
e21abd0
Merge remote-tracking branch 'origin/feat/epitrope-authz' into feat/p…
forkwright Sep 25, 2026
625a373
Merge remote-tracking branch 'origin/main' into feat/phylake-custody
forkwright Sep 25, 2026
efb2cb0
Merge remote-tracking branch 'origin/main' into pr5-merge
forkwright Sep 25, 2026
7ba4eee
fix(phylake): keep drawn key bytes in zeroizing storage
forkwright Sep 25, 2026
a98588e
Merge remote-tracking branch 'origin/main' into pr6-merge2
forkwright Sep 25, 2026
3bbf453
fix(phylake): adopt the uninitialized entropy trait and the denied axis
forkwright Sep 25, 2026
f929c6a
Merge remote-tracking branch 'origin/feat/phylake-crypto' into feat/p…
forkwright Sep 25, 2026
078ad9a
Merge remote-tracking branch 'origin/feat/epitrope-authz' into feat/p…
forkwright Sep 25, 2026
a977cc5
fix(phylake): refuse a second publish of one artifact id
forkwright Sep 25, 2026
643f218
fix(phylake): record release reasons exactly and close review gaps
forkwright Sep 25, 2026
c16970c
chore(phylake): merge main into feat/phylake-custody
forkwright Sep 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
247 changes: 245 additions & 2 deletions Cargo.lock

Large diffs are not rendered by default.

13 changes: 11 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,8 @@ syntheke = { path = "crates/syntheke", version = "0.1.6" }
rustix = { version = "1.1.5", default-features = false }
# WHY snafu: the fleet error library (kanon RUST.md); thiserror is banned.
snafu = "0.9.2"
# WHY tempfile (dev only): tests that bind a unix socket path create it in a
# fresh temporary directory.
# WHY tempfile (dev only): tests that bind a unix socket path or write key
# and store files create them in a fresh temporary directory.
tempfile = "3.27"
# WHY tokio: the daemon's async runtime and Unix socket server. The
# workspace entry enables no features; each crate names what it uses.
Expand Down Expand Up @@ -136,6 +136,15 @@ hmac = { version = "0.13", features = ["zeroize"] }
secrecy = "0.10"
sha2 = { version = "0.11", default-features = false, features = ["zeroize"] }
zeroize = "1.8"
# WHY fjall: the custody store's substrate under the named STORAGE-TIERS
# migration exception (docs/design/custody-store.md, D17.14). Version 3
# offers a single-writer transactional database whose commit takes a
# persist mode. Default features are off: the store seals every value, so
# lz4 compression would only spend CPU on ciphertext.
fjall = { version = "3.1", default-features = false }
# WHY epitrope: phylake runs epitrope's transition table, reservation
# arithmetic, and recovery rules inside its transactions.
epitrope = { path = "crates/epitrope", version = "0.1.6" }

[profile.dev.package."*"]
opt-level = 2
Expand Down
4 changes: 4 additions & 0 deletions crates/phylake/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,17 @@ repository.workspace = true

[dependencies]
chacha20poly1305.workspace = true
epitrope.workspace = true
fjall.workspace = true
getrandom.workspace = true
hkdf.workspace = true
hmac.workspace = true
rkyv.workspace = true
rustix = { workspace = true, features = ["std", "fs", "process"] }
secrecy.workspace = true
sha2.workspace = true
snafu.workspace = true
syntheke.workspace = true
zeroize.workspace = true

[dev-dependencies]
Expand Down
7 changes: 7 additions & 0 deletions crates/phylake/src/crypto/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ pub use seal::{
};
pub use wrap::{WRAPPED_KEY_LEN, WRAPPED_KEY_VERSION};

pub(crate) use seal::seal_with;

/// Length of every symmetric key and subkey in bytes.
pub const KEY_LEN: usize = 32;

Expand Down Expand Up @@ -180,6 +182,11 @@ impl Keyspace {
pub struct BlobAddress([u8; 32]);

impl BlobAddress {
/// An address read back from a sealed record.
pub(crate) const fn from_bytes(bytes: [u8; 32]) -> Self {
Self(bytes)
}

/// The address bytes.
#[must_use]
pub const fn as_bytes(&self) -> &[u8; 32] {
Expand Down
183 changes: 183 additions & 0 deletions crates/phylake/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,189 @@ pub enum Error {
#[snafu(implicit)]
location: snafu::Location,
},

/// The custody database failed a read, write, or commit.
#[snafu(display("custody database operation failed"))]
Database {
/// Underlying storage error (external, so named `error`).
#[snafu(source)]
error: fjall::Error,
/// Source location where the error was raised.
#[snafu(implicit)]
location: snafu::Location,
},

/// Creating or inspecting the store directory failed.
#[snafu(display("store directory {}: I/O failure", path.display()))]
StoreIo {
/// Directory that was accessed.
path: PathBuf,
/// Underlying I/O error (external, so named `error`).
#[snafu(source)]
error: io::Error,
/// Source location where the error was raised.
#[snafu(implicit)]
location: snafu::Location,
},

/// Creating a store at a path that is not an empty directory.
#[snafu(display("store path {} is not an empty directory", path.display()))]
StoreExists {
/// Path that was requested.
path: PathBuf,
/// Source location where the error was raised.
#[snafu(implicit)]
location: snafu::Location,
},

/// Opening a store at a path that holds none, or holds an incomplete one.
#[snafu(display("no custody store at {} ({missing} is absent)", path.display()))]
StoreMissing {
/// Path that was opened.
path: PathBuf,
/// The first part found missing.
missing: &'static str,
/// Source location where the error was raised.
#[snafu(implicit)]
location: snafu::Location,
},

/// A plaintext `meta` field has an impossible value.
#[snafu(display("store metadata field {field} is malformed"))]
MetaMalformed {
/// The field.
field: &'static str,
/// Source location where the error was raised.
#[snafu(implicit)]
location: snafu::Location,
},

/// The store was written by a newer schema than this build reads. It
/// is never opened optimistically.
#[snafu(display("store schema version {found} is newer than supported version {supported}"))]
SchemaTooNew {
/// Version recorded in the store.
found: u32,
/// Version this build writes.
supported: u32,
/// Source location where the error was raised.
#[snafu(implicit)]
location: snafu::Location,
},

/// The store was written by an older schema and needs the `migrate`
/// command before this build opens it.
#[snafu(display("store schema version {found} requires migration to version {supported}"))]
MigrationRequired {
/// Version recorded in the store.
found: u32,
/// Version this build writes.
supported: u32,
/// Source location where the error was raised.
#[snafu(implicit)]
location: snafu::Location,
},

/// A record could not be encoded.
#[snafu(display("record encoding failed"))]
Encode {
/// Underlying serializer error (external, so named `error`).
#[snafu(source)]
error: rkyv::rancor::Error,
/// Source location where the error was raised.
#[snafu(implicit)]
location: snafu::Location,
},

/// An opened record failed archive validation.
#[snafu(display("record in keyspace {keyspace} failed validation"))]
Decode {
/// Keyspace the record was read from.
keyspace: &'static str,
/// Underlying validator error (external, so named `error`).
#[snafu(source)]
error: rkyv::rancor::Error,
/// Source location where the error was raised.
#[snafu(implicit)]
location: snafu::Location,
},

/// Records that must agree do not: a reference points at nothing, or
/// a stored key has an impossible shape.
#[snafu(display("store records are inconsistent: {what}"))]
Inconsistent {
/// What was found wrong.
what: &'static str,
/// Source location where the error was raised.
#[snafu(implicit)]
location: snafu::Location,
},

/// An authorization decision or ledger computation failed, including a
/// lifecycle step the transition table forbids.
#[snafu(display("authorization rule refused the operation"))]
Authz {
/// The authorization crate's error.
#[snafu(source(from(epitrope::Error, Box::new)))]
source: Box<epitrope::Error>,
/// Source location where the error was raised.
#[snafu(implicit)]
location: snafu::Location,
},

/// A settlement named an outcome that does not fit the invocation's
/// state: success before publish, or a failure after it.
#[snafu(display("settlement outcome does not fit invocation state {state}"))]
SettleMismatch {
/// The invocation's state.
state: syntheke::InvocationState,
/// Source location where the error was raised.
#[snafu(implicit)]
location: snafu::Location,
},

/// The named invocation has no record.
#[snafu(display("invocation {invocation} has no record"))]
InvocationMissing {
/// The invocation.
invocation: syntheke::InvocationId,
/// Source location where the error was raised.
#[snafu(implicit)]
location: snafu::Location,
},

/// The named tenant is not registered.
#[snafu(display("tenant {tenant} is not registered"))]
TenantMissing {
/// The tenant.
tenant: syntheke::TenantId,
/// Source location where the error was raised.
#[snafu(implicit)]
location: snafu::Location,
},

/// A record with the caller-chosen id already exists with different
/// content.
#[snafu(display("a different {what} already exists under that id"))]
Conflict {
/// The kind of record.
what: &'static str,
/// Source location where the error was raised.
#[snafu(implicit)]
location: snafu::Location,
},

/// A failpoint simulated a crash at a lifecycle boundary.
#[snafu(display("injected crash {phase} {boundary}"))]
InjectedCrash {
/// The boundary.
boundary: crate::store::Boundary,
/// Before or after the commit.
phase: crate::store::Phase,
/// Source location where the error was raised.
#[snafu(implicit)]
location: snafu::Location,
},
}

/// Result alias for `phylake` operations.
Expand Down
13 changes: 8 additions & 5 deletions crates/phylake/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,18 @@
//! transaction so that restart recovery can settle or release each
//! invocation exactly once.
//!
//! This slice provides the encryption layer (`docs/design/custody-store.md`,
//! "Encryption at rest"): [`keyfile`] loads and creates the root key, and
//! [`crypto`] derives subkeys, seals records, wraps tenant data keys,
//! computes keyed blob addresses, and checks the key-check value that gates
//! a locked start. The keyspaces themselves land in a later slice.
//! [`keyfile`] loads and creates the root key; [`crypto`] derives subkeys,
//! seals records, wraps tenant data keys, computes keyed blob addresses,
//! and checks the key-check value that gates a locked start
//! (`docs/design/custody-store.md`, "Encryption at rest"). [`store`] holds
//! the keyspaces, the invocation transactions B1 through B5, failure
//! injection, and restart recovery.
#![deny(missing_docs)]

pub mod crypto;
mod error;
pub mod keyfile;
pub mod store;

pub use error::{Error, Result};
pub use store::{Store, StoreOptions};
Loading
Loading