Keep state and keys out of the shared repo
The dev VM is treated as untrusted, and the repo is a read-write virtiofs mount shared with the backend host -- so a CA private key sitting in it is a key that machine can sign with, and a leaf signed by this CA is one the phone's pinned app accepts without question. Pinning against a CA the attacker holds is no pinning at all. So certificates are now generated on the machine that serves them, into $XDG_CONFIG_HOME/ai-app/certs at 0700 with 0600 keys (AI_APP_CERTS overrides), and config.json and session transcripts move to the XDG config and data directories. Transcripts move for a plainer reason than the keys: they are whole conversations, and they were world-readable at 0644. Two smaller things fall out. The host and VM stop sharing one config, which had already put a test token on the production backend. And state stops living where `git clean -xdf` would take the enrollment and every transcript with it. State that predates the move is still read from the repo, with a warning naming where to move it, so an existing install keeps working rather than silently coming up on an empty config -- the precedence is covered by a test, since picking the wrong file would otherwise be silent. Verified: 31 tests, clippy clean; the certificate script writing 0700/0600 into an overridden directory; and the server logging the fallback and serving from it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017xn8nHw1tw1R6PtiY1eEtw
This commit is contained in:
1 parent
fff1fb49e8
commit
d2d2832ec8
8 files changed
+248
-37
No files matched your search
@@ -147,7 +147,7 @@ impl LiveSession {
|
||||
};
|
||||
let name = format!("{}.{extension}", random_hex());
|
||||
let dir = self.dir().join("attachments");
|
||||
std::fs::create_dir_all(&dir).with_context(|| format!("create {}", dir.display()))?;
|
||||
crate::config::create_private_dir(&dir)?;
|
||||
std::fs::write(dir.join(&name), bytes)
|
||||
.with_context(|| format!("write attachment {name}"))?;
|
||||
Ok(name)
|
||||
@@ -189,8 +189,7 @@ impl SessionManager {
|
||||
/// session spawns its event pump).
|
||||
pub fn new(config_path: PathBuf, data_dir: PathBuf) -> Result<Self> {
|
||||
let config = Config::load(&config_path)?;
|
||||
std::fs::create_dir_all(&data_dir)
|
||||
.with_context(|| format!("create {}", data_dir.display()))?;
|
||||
crate::config::create_private_dir(&data_dir)?;
|
||||
|
||||
let mut live = HashMap::new();
|
||||
for meta in &config.sessions {
|
||||
@@ -452,7 +451,7 @@ fn launch(
|
||||
data_dir: &Path,
|
||||
) -> Result<Arc<LiveSession>> {
|
||||
let dir = data_dir.join(&meta.id);
|
||||
std::fs::create_dir_all(&dir).with_context(|| format!("create {}", dir.display()))?;
|
||||
crate::config::create_private_dir(&dir)?;
|
||||
let transcript_path = dir.join("transcript.jsonl");
|
||||
let transcript = Transcript::open(&transcript_path)?;
|
||||
|
||||
|
||||
Reference in new issue
Block a user