docs(org): complete A5 living-docs sweep (item CRUD merged) + dead_code cleanup

Extends the A5 pre-stage now that dev-b's full B-stream (item CRUD + all 19
org subcommands) merged to main (7392795). Living docs:
- FORMATS/CRYPTO/SECURITY/DESIGN: flip the item-CRUD "pending Dev-B" markers to
  shipped; SECURITY audit vocabulary moves item-* actions to live.
- crates/relicario-cli/ARCHITECTURE.md: full 19-subcommand surface (12 admin +
  7 item CRUD), accurate OrgAddKind scope (Login/SecureNote/Identity).
- STATUS.md: enterprise-org-vault landed section (merged 7392795) + tracked
  follow-ups + honest known-limitations; correct spec citation.
- ROADMAP.md: backend-complete row + phase-2 follow-ups.
- CHANGELOG.md: finalize the enterprise-org-vault Unreleased section (item CRUD
  into Added; Card/Key/Document/Totp + extension + phase-2 into Deferred).

Code (PM-directed dead_code fixes): wire device::current_device_seed by removing
the identical duplicate private fn in org_session.rs (de-dup); #[allow(dead_code)]
+ justification on org_session org_meta_path/load_meta (API completeness, no
command consumes org.json yet). Also silence a 3rd pre-existing test-only warning
(unused relicario() helper in tests/org_init_signing.rs).

Honest deferrals kept explicit throughout: Card/Key/Document/Totp org add/edit
parity, extension org switch/read (Dev-D) + writes, phase-2 (SSO/LDAP, read
audit, per-collection subkeys, HTTP plane). Full workspace cargo test green,
zero warnings. All cited code constants pinned file:line.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TJo44YM3UbBjro2fG6NrKy
This commit is contained in:
adlee-was-taken
2026-06-20 15:54:51 -04:00
parent 8bb1d779c4
commit 0cd417ded7
10 changed files with 123 additions and 66 deletions

View File

@@ -39,8 +39,14 @@ impl UnlockedOrgVault {
}
pub fn members_path(&self) -> PathBuf { self.root.join("members.json") }
pub fn collections_path(&self) -> PathBuf { self.root.join("collections.json") }
// OrgMeta accessors — part of the UnlockedOrgVault path/loader API surface
// (parallel to members_path/collections_path + load_members), retained for
// completeness. No command consumes org.json yet; surfacing the org
// name/id in `org status` is a tracked follow-up, so allow until then.
#[allow(dead_code)]
pub fn org_meta_path(&self) -> PathBuf { self.root.join("org.json") }
#[allow(dead_code)]
pub fn load_meta(&self) -> Result<OrgMeta> {
let s = fs::read_to_string(self.org_meta_path()).context("read org.json")?;
Ok(serde_json::from_str(&s).context("parse org.json")?)
@@ -185,7 +191,7 @@ pub fn open_org_vault(dir_flag: Option<&std::path::Path>) -> Result<UnlockedOrgV
fs::read(&key_path).with_context(|| format!("read {}", key_path.display()))?;
// Recover the device ed25519 seed and unwrap.
let seed = current_device_seed()?;
let seed = crate::device::current_device_seed()?;
let org_key = relicario_core::unwrap_org_key(&wrapped, &seed)?;
Ok(UnlockedOrgVault { root, org_key })
@@ -202,27 +208,6 @@ fn current_device_fingerprint() -> Result<String> {
}
/// Recover the active device's ed25519 seed (the 32-byte private scalar source)
/// from its OpenSSH `signing.key`, for ECIES unwrap.
fn current_device_seed() -> Result<Zeroizing<[u8; 32]>> {
let name = crate::device::current_device()?
.ok_or_else(|| anyhow::anyhow!("no active device — run `relicario device add` first"))?;
let key_pem = crate::device::load_signing_key(&name)?;
let private = ssh_key::PrivateKey::from_openssh(key_pem.as_str())
.map_err(|e| anyhow::anyhow!("parse device signing key: {e}"))?;
let ed = private
.key_data()
.ed25519()
.ok_or_else(|| anyhow::anyhow!("device signing key is not ed25519"))?;
// Ed25519PrivateKey derefs to its 32-byte seed.
let seed_bytes: &[u8] = ed.private.as_ref();
if seed_bytes.len() != 32 {
anyhow::bail!("ed25519 seed has wrong length: {}", seed_bytes.len());
}
let mut seed = Zeroizing::new([0u8; 32]);
seed.copy_from_slice(seed_bytes);
Ok(seed)
}
pub(crate) fn atomic_write(path: &Path, data: &[u8]) -> Result<()> {
let mut tmp = path.as_os_str().to_owned();
tmp.push(".tmp");