OXYGENHUB

Tags: an unsigned repository keeps the tag message by storing the attestation unsigned; unsigned attestations are not advertised to remotes c792033af344

Author
Andy McCall @andymccall <[email protected]>
When
· 2026-09-27 16:02 UTC
Signature
✔ signed by Andy McCall
Hash
c792033af3441e2bd3388ea1dc814fcd218c4df654a2738e2bb172b7a77ea54a
Depends on
132b884b9a8d 728da0f92161 982864068a59
Depended on by
387335ff7f4d 75167672ebcd
In bookmarks
agents-in-readme bare-hashes carried-not-removed carry-identical doctor-trust identical-sides keyless-tags main merge-remote-bookmark only-help-103 paths-for-resolutions-and-amend push-notes show-stat subrepo-pin-state switch-says
In tags
v0.1.0 v0.10.0 v0.11.0 v0.2.0 v0.3.0 v0.3.1 v0.4.0 v0.4.1 v0.4.2 v0.4.3 v0.4.4 v0.4.5 v0.4.6 v0.5.0 v0.5.1 v0.6.0 v0.7.0 v0.8.0 v0.9.0
Edits
6 atoms, 0 tree operations, 2 files

Browse the tree at this change

modified crates/oxy-core/src/repo/tags.rs +9 −2
…
50 unchanged lines
51 51
        }
52 52
        let state = self.store.put_state(&StateObject::new(heads.clone()))?;
53 53
        let mut signer = None;
54 +
        let attest = Attest { repo: self.id()?, hlc: Hlc::now(), body: AttestBody::Tag { name: name.into(), state, message: message.into() } };
54 55
        if self.secret_key()?.is_some() {
55 -
            let sa = self.sign_attest(&Attest { repo: self.id()?, hlc: Hlc::now(), body: AttestBody::Tag { name: name.into(), state, message: message.into() } })?;
56 +
            let sa = self.sign_attest(&attest)?;
56 57
            let r = sa.attest.hash();
57 58
            self.set_tag_attest(name, &r)?;
58 59
            signer = sa.sigs.first().and_then(|s| self.store.get_identity(&s.key).ok()).map(|i| i.name);
60 +
        } else {
61 +
            // No key: keep the attestation unsigned so the message survives;
62 +
            // it is never sent to remotes (`tag_refs`).
63 +
            let r = self.store.put_attest(&attest)?;
64 +
            self.set_tag_attest(name, &r)?;
59 65
        }
60 66
        let mut view = op.view.clone();
61 67
        view.tags.insert(name.into(), state);
…
34 unchanged lines
96 102
    pub(crate) fn tag_refs(&self) -> Result<Vec<crate::protocol::TagRef>> {
97 103
        let mut out = Vec::new();
98 104
        for t in self.tags()? {
99 -
            out.push(crate::protocol::TagRef { name: t.name.clone(), state: t.state, heads: t.heads, attestation: self.tag_attest(&t.name)?.map(|(_, a)| a) });
105 +
            let attestation = self.tag_attest(&t.name)?.map(|(_, a)| a).filter(|a| !a.sigs.is_empty());
106 +
            out.push(crate::protocol::TagRef { name: t.name.clone(), state: t.state, heads: t.heads, attestation });
100 107
        }
101 108
        Ok(out)
102 109
    }
…
43 unchanged lines
modified crates/oxy-core/src/repo/tests.rs +15 −0
…
2532 unchanged lines
2533 2533
    assert!(err.contains("empty") || err.contains("already"), "{err}");
2534 2534
}
2535 2535
2536 +
#[test]
2537 +
fn unsigned_tags_keep_their_message() {
2538 +
    hermetic();
2539 +
    let dir = tempfile::tempdir().unwrap();
2540 +
    let root = dir.path();
2541 +
    let repo = Repo::init(root).unwrap().with_home(root.join("home"));
2542 +
    write(root, "a.txt", b"a\n");
2543 +
    repo.commit("a", &[]).unwrap();
2544 +
    assert!(repo.secret_key().unwrap().is_none());
2545 +
    repo.tag_create("v1", &repo.bookmark_heads().unwrap(), "first release").unwrap();
2546 +
    let tags = repo.tags().unwrap();
2547 +
    assert_eq!((tags[0].name.as_str(), tags[0].message.as_str(), tags[0].signer.as_deref()), ("v1", "first release", None));
2548 +
    assert!(repo.tag_refs().unwrap()[0].attestation.is_none(), "an unsigned attestation is not advertised");
2549 +
}
2550 +