Repository navigation
@truealter/sdk@0.5.15 is staged on npm and reaches installers when a maintainer approves it with 2FA.
npm install @truealter/sdk
Fixed
-
The canonical form enforces the profile it publishes.
canonicalJsonand
canonicalStringifynow refuse nesting deeper than 64 containers and an
integer past 2^53-1 in magnitude, both of which
vectors/canonical-jcs-v1.jsonhas pinned as refusals since 0.5.13. Before,
nesting at 65 canonicalised and{"a":9007199254740992}produced a digest
the Python runtime refuses to compute. A parsed number no longer says
whether it was written as an integer, so on a parsed value the refusal
covers every integral number from 2^53 up to 1e21, the range
JSON.stringifywrites back out as integer digits. Doubles at 1e21 and
above, which reach the wire in exponent form, are still admitted. Both
functions also refuseundefined, functions, bigints, and instances such
asDateandMapthat would otherwise canonicalise to{}, which is not
whatJSON.stringifysends for them. Every refusal is an
AlterCanonicalizationErrorcarrying a stablerefusalcode.Reported from outside, by Iman Schrock of EMILIA Protocol against 0.5.14
from a clean install, together with the duplicate-member and invocation
findings below. -
Signed invocations carry
expandjti, andaudon request.
signInvocationemittedtool,args_sha256,nonce,iatandiss
only, so an invocation never expired and carried no id a verifier could
hold against replay. It now emitsexp,iatplus a lifetime of 120
seconds by default (ttlSeconds, 1 to 3600), andjti, 128 random bits in
base64url. It emitsaudwhen given anaudience. The audience is
optional because a verifier that does not expect one refuses a token that
names one, andMCPClientdoes not set it when it signs.
Added
-
parseStrictWire, a strict parser for wire text. Duplicate member names
can only be seen in the text:JSON.parsekeeps the last of two and
discards the first, so{"a":1,"a":2}reached every canonicaliser as
{"a":2}.parseStrictWirereads the text itself and refuses a duplicate
member at any depth, compared after escape decoding, an integer literal past
2^53-1, a non-finite number, nesting past 64, an unpaired surrogate,
malformed JSON and trailing content, each with its ownrefusalcode:
duplicate_member,unsafe_integer,non_finite_number,depth_bound,
unpaired_surrogate,malformed_json,trailing_content. Anything read off
the wire to be hashed or signed should be parsed with it rather than with
JSON.parse. It refuses everything the Python runtime'sparse_strict_wire
refuses. -
canonicalizeWireText, the strict parse and the canonical form in one
step. It is the only path that can canonicalise a double such as1e20,
which the profile admits because it is written as a double, and it runs the
whole vector suite, every accept and every refusal. None is skipped any
more. -
verifyInvocation, which verifies a signed invocation and requires
aud,expandjti.audmust equal the caller'sexpectedAudience,
expmust be present, unexpired allowing 60 seconds of clock skew, and no
more than an hour afteriat, andjtimust be present. The header and
payload are read with the strict parser, so a repeated claim is refused
rather than resolved to its last value. Replay protection is the caller's,
through anisReplayhook called only once every other check has passed;
the verifier stores nothing and says inreplayCheckedwhether the hook
ran. A refusal returns a stablereason.verifyProvenanceis unchanged. -
CANONICAL_MAX_DEPTH,CANONICAL_MAX_SAFE_INTEGER,
INVOCATION_DEFAULT_TTL_SECONDS,INVOCATION_MAX_TTL_SECONDSand
INVOCATION_CLOCK_SKEW_SECONDS, the bounds above as constants.