# Public API
```rust
use kcode_k1_transaction_store::TxId;
pub const SUBSYSTEM_BYTES: usize = 20;
pub const PUBLIC_KEY_BYTES: usize = 32;
pub const SIGNATURE_BYTES: usize = 64;
pub const MIN_TRANSACTION_BYTES: usize = 136;
pub const GENESIS_PARENT: TxId = TxId::from_bytes([0xff; 12]);
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct SubsystemId;
impl SubsystemId {
pub fn from_bytes(bytes: [u8; SUBSYSTEM_BYTES]) -> Result<Self, String>;
pub fn from_str(value: &str) -> Result<Self, String>;
pub const fn as_bytes(&self) -> &[u8; SUBSYSTEM_BYTES];
pub fn as_str(&self) -> &str;
}
pub struct Transaction<'a>;
impl<'a> Transaction<'a> {
pub fn parse(bytes: &'a [u8]) -> Result<Self, String>;
pub fn parent(&self) -> TxId;
pub fn timestamp(&self) -> u64;
pub fn creator(&self) -> &[u8; PUBLIC_KEY_BYTES];
pub fn subsystem(&self) -> SubsystemId;
pub fn payload(&self) -> &'a [u8];
pub fn signature(&self) -> &'a [u8; SIGNATURE_BYTES];
pub fn signing_bytes(&self) -> &'a [u8];
}
pub fn build_signed_transaction<F>(
parent: TxId,
timestamp: u64,
creator: [u8; PUBLIC_KEY_BYTES],
subsystem: SubsystemId,
payload: &[u8],
signer: F,
) -> Result<Vec<u8>, String>
where
F: FnOnce(&[u8]) -> Result<[u8; SIGNATURE_BYTES], String>;
```
`SUBSYSTEM_BYTES`, `PUBLIC_KEY_BYTES`, and `SIGNATURE_BYTES` are the exact field widths. `MIN_TRANSACTION_BYTES` is the wire length with an empty payload. `GENESIS_PARENT` is the all-`0xff` sentinel and does not identify a real transaction.
`SubsystemId` is valid UTF-8 occupying exactly 20 bytes, with no padding or terminator. Both constructors reject values whose encoded byte length is not exactly 20; `from_bytes` also rejects invalid UTF-8.
A transaction has this wire layout:
| Byte range | Value |
| --- | --- |
| `0..12` | Parent transaction ID |
| `12..20` | Little-endian `u64` Unix timestamp in seconds |
| `20..52` | Ed25519 public key |
| `52..72` | Subsystem ID |
| `72..len - 64` | Payload |
| `len - 64..len` | Ed25519 signature |
Transactions are at least 136 bytes, and an empty payload is valid. The signature covers every preceding byte; `signing_bytes` returns that exact prefix.
`Transaction::parse` borrows its input and validates only minimum length and subsystem UTF-8. It does not validate the signature, creator or leader authority, timestamp policy, payload, parent existence, storage, or ingress policy.
`build_signed_transaction` accepts every payload whose final `136 + payload.len()` byte output is representable and allocatable. It requests one output `Vec` with that exact final capacity, writes the parent, little-endian timestamp, creator, subsystem, and payload, then invokes `signer` exactly once with that exact prefix. A successful signature is appended unchanged. A signer error is returned unchanged and the partial output is discarded. The function performs no signature verification, clock or key lookup, authority decision, storage, or other I/O. Length overflow returns an error; allocation failure follows `Vec` behavior.
All operations are synchronous and have no shared mutable state, locks, queues, background work, persistence, internal retries, or timeouts. Independent calls do not coordinate unless caller-supplied signer code does so. Parsing, constructors, and accessors perform bounded constant work independent of transaction and payload length; successful calls allocate no memory, while errors may allocate only their returned `String`. The builder performs `O(payload.len())` work, copies the payload once, requests one `136 + payload.len()` byte allocation, and does not reallocate. The signer runs inline and may block indefinitely; its own computation, allocation, I/O, synchronization, and side effects are caller-owned. No wall-clock guarantee is made for builder or signer execution.