Repository navigation
v0.0.3-alpha
The third release, and the one where a Secret sink is written rather than promised.
v0.0.2-alpha accepted --secret-out <path>, performed the Step, wrote the declared-secret field
to the Store as the constant marker, and discarded the value at exit 0. Nothing leaked; the
failure mode was loss, and every guarantee held while the only thing the operator invoked for did
not happen. This release writes the file — and, on the way, decided what the file is.
go install github.com/TheLoomLabs/hyper/cmd/hyper@v0.0.3-alphaUpgrading from v0.0.2-alpha
Install the new binary, run hyper project, read the diff. project rewrites the version pin and
the release digest, both land in a file you review, and nothing else on the machine changes. Until
you do, every command Refuses version-pin-mismatch at exit 77 — the gate compares the pinned
version against the binary's for exact equality, and that is working as intended.
Read the four breaking changes below before you upgrade a repository that runs. Two of them can
turn a Run that completed into a Refusal, one changes what a shell mutate puts in the Store, and
one renames a member on two wires.
Breaking
- An effectful
shellOperation records that it ran and not what it printed. The built-in
shellProvider'smutate,mutate_onceandmutate_skip_if_recordednow projectexit_code
and nothing else;readstill projectsstdoutandstderrbeside it. A shellmutate's Record
is an Asset and Compaction never removes one, so a command that printed a credential put it in the
Store permanently — andshellis the Opaque Capability, wherehyperauthored the Manifest and
knows nothing whatever about the command. An exit code is a fact about the callhyperowns.
Breaking for a Procedure whoserequire:predicates on an effectful Step'sstdout: the path
now resolves to nothing and the predicate has no root. Reach for areadStep beside themutate.
For a credential a command printed before this, rotation is still the only remedy that is one.
(ADR-0143) - An empty credential is its own Refusal, on both surfaces.
export HCLOUD_TOKEN=''passed the
§6 gate: the header was composed present and blank, the endpoint answered401, and that landed as
failure at1— the world resisting — where the invocation was never ready. On an effectful
Procedure the Steps before the first authenticated one had already run. A credential is now read
under a closed three:absentRefusescredential-absent,emptyRefusescredential-empty,
setresolves. On both wirespresent(boolean) becomespresence(the three words) —
renamed rather than re-typed, so a client written against the boolean finds no member rather than
reading"absent"as true. (#264, ADR-0145) - A Run that cannot write a secret Refuses instead of losing it, and
--secret-outis a
directory.<sink>/<nnnn>/<name>/<field>,0600under a0700directory, holding the value's
own bytes and nothinghyperadded. One Run produces more than one secret in three independent
ways — two secret-producing Steps in one Procedure, one Step that expands, an Operation declaring
twosecret:fields — so the addressable thing is the triple Step, Record, field, and the shape
that expresses it with no key to design and no parser to run is the filesystem's. The path must
not already exist:hypermakes it, which is what puts the mode inhyper's hands and what
stops an earlier Run's file being read as this one's. Two Refusals guard it, both at Run start and
both77:secret-sink-absent, no sink named where a Step declares secret output, and
secret-sink-unfilled, a sink named that nothing in the Procedure can fill.
(#266,
#270,
#275, ADR-0146, ADR-0148, ADR-0151) checkrefuses arecord: fields:value that is not a scalar. §3 has said those values stay
uniformly scalar since it was written — which is what keeps a mapping there meaning a reference and
nothing else — and nothing held it:fields:is Open, so the schema declined to have an opinion,
and the projection reader drops what it cannot read because judging a Manifest ischeck's work
and never a reader's. A Manifest that checked clean underv0.0.2-alphacan now fail, under
schema-mismatchat that field's own position. It is the spelling that cost a sealed session two
live credentials:{path: $.body.token, secret: true}reads as a marking and is dropped by the
projection, and the message names where the declaration actually goes.
(#275, ADR-0151)
error_code goes from fifty-one members to fifty-three, gaining credential-empty and
secret-sink-unfilled. The set is closed and a consumer matches on it, so the count is stated here
rather than left to be discovered.
Added
secret:is named on every surface that renders an Operation. Four rendered one and none named
the key it declares secret output under — a gap measured rather than guessed: a sealed session that
had to author one got the spelling wrong and spent thirteenBashcalls finding it, eight of them
stringsandnmoverbin/hyper, which is a user's own reading of a tool that ships no
specification on their machine.provider's summary gains a clause,operation's derived block
gainssecret_fields,reviewmarks the Operation's key line and indexes it under aSECRET
flag, and the orientationhyper projectwrites states where the key goes and where it does not.
The next sealed run authored it correctly at its twenty-first call and went looking nowhere.
(#276, ADR-0152, ADR-0153)- A Target declaration may withhold a variable
hyperhas no position for.hyperremoves a
credential from ashellchild's environment by the position it occupies — a declaration's
credential slot — so a secret the repository never named passes to any command and appears on no
surface. The process environment is the interface every secret manager speaks, so wrapping the
invocation is the vault integrationhyperhas, andop run -- hyper runleaves the wrapper's own
token beside the one it fetched — strictly more powerful than the credential it was there to
produce.withhold:is a list of plain variable names; the set a child is denied is the union over
every declaration in the repository of the slots and thewithhold:entries. It grants nothing,
which is what makes it cheap. (ADR-0144) - A skipped member's absent secret is a line in the Run's report. A
skip-if-recordedStep whose
member the Store already holds concludes without a call, produces no value, and so leaves nothing
in the sink for that member.checkgains no member for the combination — the rule would refuse a
Manifest that works, and mint a credential only where we hold no record of one is a thing an
author means — so the Run says it instead, per member, where a Step that skipped two and called for
one would otherwise renderranand be byte-identical to a Step that wrote all three.
(#273, ADR-0150)
Fixed
probeno longer renders a declared-secret field. ADR-0017 argued the wire is visible from the
request — a Probe isreadKind againstlocal, holds no credential and writes no Record — and a
secret:field is a second position that arrives in the response. It reached the FIELD/VALUE
table andprobe_result.response: a terminal, an Actions log, and the tool result an agent reads
back. The projection now carries the constant marker in each declared position, which is the
suppression a Run already makes on the way to the Store, and the response object is withheld whole
rather than blanked at the positionssecret:names. (ADR-0142)OperationInfoanswers declares secret output once. Three members carried the fact, two
callers read different ones, and a comment named the wrong one as canonical. One member answers it
now and the reading is derived. Visible only in this: asecret:item that is not a name no longer
drawscadence-secret-outputbeside theschema-mismatchit earns — a declaration the walk could
not read states nothing. (#278, ADR-0154)
Documentation
- The README carries every command with its flags, and the MCP server wired the way it actually
goes in. SECURITY.mdsays what the vault integration is: the process environment, and a wrapper around
the invocation — which is whatwithhold:above exists to make safe.- §9's two-member Refusal example renders what
hyperprints, and neither §9 nor theruntool's
schema describes a0600write that nothing performed. docs/adr/holds 154 records.
What is in the release
Four archives — x86_64-linux, aarch64-linux, x86_64-darwin, aarch64-darwin — each holding
one file, hyper, plus checksums.txt, which is sha256sum's own output over all four. There is
no Windows build.
Nobody signed or notarised the macOS archives. Run the binary from a shell and it runs; a copy
downloaded in a browser carries the quarantine attribute the browser set, and Finder will refuse to
open it and offer Move to Trash. That is the attribute, not a verdict on the bytes.
hyper is alpha. It has one built-in Provider, shell, it never updates itself, and the
specification in docs/spec/ is the
authority where the code disagrees with it.