Goal. Show an end-to-end stamp → verify → (optional) anchor path using Python stdlib only. All math and comparisons are plain ASCII and deterministic.
A. Stamp (essential steps)
- Read file bytes and hash
# bytes -> digest (default sha256; streaming OK)
h_file = sha256(file_bytes)
- Fix
iso_utcand compute epoch seconds
iso_utc = "YYYY-MM-DDTHH:MM:SSZ" # exact Z, no subseconds, no offsets
unix_seconds = seconds_since_1970_UTC(iso_utc)
- Deterministic clock from UTC
wrap360(x) = x - 360*floor(x/360)
theta_deg = wrap360( (unix_seconds / 86400) * 360 )
theta_str = print_fixed(theta_deg, digits=theta_prec, rounding="half-even") # default theta_prec=5
rasi_idx = floor(theta_deg / 30)
- Build
stamp_coreand chain
stamp_core = "SSMCLOCK1|" + iso_utc + "|" + rasi_idx + "|" + theta_str + "|" + h_file_hex
chain_0 = "0"*64
chain_k = sha256( ascii(chain_{k-1} + "|" + stamp_core) ) # or H_chain per kv:chain_algo
- Emit one ASCII line
SSMCLOCK1|iso_utc|rasi_idx|theta_str|h_file_hex|chain_hex[|kv:...]
Defaults if kv: absent: algo=sha256;chain_algo=sha256;theta_prec=5;float=ieee75464;time_mode=derived_utc.
B. Verify (essential steps)
- Parse fields and re-hash
h_prime = H_algo(file_bytes) # from kv:algo or sha256
require h_prime == recorded_hex -> HASH_OK=true
- Recompute clock from
iso_utc
theta_deg' = wrap360( (unix_seconds(iso_utc) / 86400) * 360 )
rasi_idx' = floor(theta_deg' / 30)
theta_str' = print_fixed(theta_deg', digits=theta_prec, rounding="half-even")
require rasi_idx' == rasi_idx_recorded
and theta_str' == theta_str_recorded # preferred
# optional numeric fallback when print-policy drift suspected:
# abs(theta_deg' - theta_deg_recorded) <= 0.5 * 10^(-theta_prec)
- Rewalk chain (if a ledger exists)
prev = "0"*64
for each row k in ledger order:
calc = H_chain( ascii(prev + "|" + stamp_core_k) )
require calc == row.chain
prev = calc
require some row matches this file's chain -> CHAIN_OK=true
# if no ledger: CHAIN_OK=na
- Anchor parity (if a day note exists)
sort stamps by (iso_utc, stamp_core, chain)
joined = "Stamp_1|...|Stamp_n"
rollup_D = sha256( ascii(joined) )
require rollup_D == rollup_sha256_in_note and counts match -> ANCHOR_OK=true
# if no note: ANCHOR_OK=na
- Flags and verdict (stdout, ASCII)
HASH_OK=true CLOCK_OK=true CHAIN_OK=true ANCHOR_OK=na EVIDENCE_OK=absent
VERDICT=PASS
C. One-minute sanity (copy-ready mini demo)
Inputs:
file bytes: "hello\n"
iso_utc_1 = "2025-10-13T10:53:57Z"
iso_utc_2 = "2025-10-13T10:53:58Z"
Expected derived values:
sha256("hello\n") = 5891b5b522d5df086d0ff0b110fbd9d21bb4fc7163af34d08286a2e846f6be03
theta(iso_utc_1) -> "163.48750" ; rasi_idx_1 = 5
theta(iso_utc_2) -> "163.49167" ; rasi_idx_2 = 5
Chaining from zero:
chain_0 = "0"*64
chain_1 = sha256( ascii(chain_0 + "|" + stamp_core_1) )
chain_2 = sha256( ascii(chain_1 + "|" + stamp_core_2) )
Daily roll-up (two stamps, canonical sort and join):
rollup_D = sha256( ascii(Stamp_1 "|" Stamp_2) )
All outputs are lowercase 64-hex, joins are with literal |, and everything is 7-bit ASCII.
D. Notes and guardrails
- UTC only:
iso_utcmust beYYYY-MM-DDTHH:MM:SSZ; stamps with:60are invalid (no stamping at23:59:60). - Binary64 math: intermediate math uses IEEE-754 binary64; print
theta_degwith exact digits and half-even rounding. - ASCII discipline:
ascii(x)for every hashed concatenation; no Unicode punctuation; no spaces around separators. - Unknown
kv:keys: must be ignored by verifiers; recognized keys must pass domain checks. - Streaming OK: large-file hashing by chunks must equal monolithic hashing.
Navigation
Back: SSM-Clock Stamp – Build & (Optionally) Sign the Verify Pack (8.2)
Next: SSM-Clock Stamp – Quick Start (9)