git-ents.gitmain
⌘K
foforge
proposal.rs98 lines · 4.2 KB · rusthistorycomment on this file
1//! The fourth argument's shape (`receive.proposal-shape`): the ref
2//! transitions a caller proposes, the new objects that accompany them, and
3//! any transport-auth evidence the frontend collected.
4
5use gix::refs::FullName;
6use gix_hash::ObjectId;
7
8/// One proposed ref transition: `(refname, old-oid, new-oid)`, exactly the
9/// triple `receive.proposal-shape` names.
10///
11/// `old` is the frontier the *proposal* claims — what a `git push` command
12/// line reports as its own base, or what a local UI last read. [`crate::receive`]
13/// never trusts it for admission — [`ents_gate::verify`] re-reads the actual
14/// current tip itself (the same snapshot every other check uses) — nor does
15/// it enforce it: unlike git's `receive-pack`, which refuses a push whose
16/// old-oid is stale, a stale `old` whose `new` still descends from the real
17/// tip applies cleanly, and one that does not is refused by the gate's own
18/// fast-forward check against the re-read tip, never by comparing this field.
19///
20/// # Examples
21///
22/// ```
23/// use ents_receive::RefTransition;
24///
25/// let transition = RefTransition {
26/// name: "refs/meta/issues/1".try_into().expect("valid"),
27/// old: None,
28/// new: Some(gix_hash::ObjectId::null(gix_hash::Kind::Sha1)),
29/// };
30/// assert!(transition.old.is_none());
31/// ```
32#[derive(Debug, Clone, PartialEq, Eq)]
33pub struct RefTransition {
34 /// The ref being updated.
35 pub name: FullName,
36 /// The tip the proposal claims as its base, or `None` for creation.
37 pub old: Option<ObjectId>,
38 /// The proposed new tip, or `None` to delete the ref.
39 pub new: Option<ObjectId>,
40}
41
42/// Transport-level authentication evidence a frontend collected: a
43/// signed-push credential, a smart-HTTP session, or nothing for a frontend
44/// whose transport carries no separate authentication (`receive.proposal-shape`).
45///
46/// This is a connection-level ACL input for `refs/heads/*` only
47/// (`gate.principled-split`) — no such ACL policy is defined yet anywhere in
48/// the spec, so [`crate::receive`] accepts and threads this value through
49/// without interpreting it; it is never substituted for the tip invariant on
50/// a `refs/meta/*` update, which is the one thing `receive.proposal-shape`
51/// actually requires today. Defining and enforcing the `refs/heads/*` ACL
52/// itself is future work with no requirement id yet to hang it on.
53#[derive(Debug, Clone, PartialEq, Eq)]
54pub struct TransportAuth {
55 /// Opaque evidence bytes: a signed-push certificate, a session token,
56 /// or whatever shape a future frontend needs. `receive` never parses
57 /// this; only a future `refs/heads/*` ACL check would.
58 pub evidence: Vec<u8>,
59}
60
61/// The fourth argument to [`crate::receive`]: every proposed ref transition
62/// this call attempts, the object ids the proposal introduces, and any
63/// transport-auth evidence (`receive.proposal-shape`).
64///
65/// # Examples
66///
67/// ```
68/// use ents_receive::{Proposal, RefTransition};
69///
70/// let proposal = Proposal {
71/// transitions: vec![RefTransition {
72/// name: "refs/meta/issues/1".try_into().expect("valid"),
73/// old: None,
74/// new: Some(gix_hash::ObjectId::null(gix_hash::Kind::Sha1)),
75/// }],
76/// objects: vec![gix_hash::ObjectId::null(gix_hash::Kind::Sha1)],
77/// auth: None,
78/// };
79/// assert_eq!(proposal.transitions.len(), 1);
80/// ```
81// @relation(receive.proposal-shape, scope=file)
82#[derive(Debug, Clone, PartialEq, Eq, Default)]
83pub struct Proposal {
84 /// The proposed ref transitions this call attempts, applied together
85 /// as one atomic batch (`arch.refstore-read-cas-split`,
86 /// `gate.atomic-cas`).
87 pub transitions: Vec<RefTransition>,
88 /// The object ids this proposal introduces, already durably present in
89 /// the object store [`crate::receive`] is handed (the frontend's job
90 /// per `receive.shared-path`: the CLI and local UI write directly, and
91 /// smart-HTTP unpacks the incoming pack, before `receive` is ever
92 /// called). `receive` checks each of these against the redaction list
93 /// at ingest time (`receive.redaction-ingest`).
94 pub objects: Vec<ObjectId>,
95 /// Transport-auth evidence the frontend collected, or `None`. See
96 /// [`TransportAuth`] for why `receive` does not interpret this today.
97 pub auth: Option<TransportAuth>,
98}