Skip to content

Releases: true-alter/sdk

v0.5.15

Choose a tag to compare

@github-actions github-actions released this 25 Sep 02:47

@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. canonicalJson and
    canonicalStringify now refuse nesting deeper than 64 containers and an
    integer past 2^53-1 in magnitude, both of which
    vectors/canonical-jcs-v1.json has 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.stringify writes back out as integer digits. Doubles at 1e21 and
    above, which reach the wire in exponent form, are still admitted. Both
    functions also refuse undefined, functions, bigints, and instances such
    as Date and Map that would otherwise canonicalise to {}, which is not
    what JSON.stringify sends for them. Every refusal is an
    AlterCanonicalizationError carrying a stable refusal code.

    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 exp and jti, and aud on request.
    signInvocation emitted tool, args_sha256, nonce, iat and iss
    only, so an invocation never expired and carried no id a verifier could
    hold against replay. It now emits exp, iat plus a lifetime of 120
    seconds by default (ttlSeconds, 1 to 3600), and jti, 128 random bits in
    base64url. It emits aud when given an audience. The audience is
    optional because a verifier that does not expect one refuses a token that
    names one, and MCPClient does 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.parse keeps the last of two and
    discards the first, so {"a":1,"a":2} reached every canonicaliser as
    {"a":2}. parseStrictWire reads 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 own refusal code:
    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's parse_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 as 1e20,
    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, exp and jti.
    aud must equal the caller's expectedAudience,
    exp must be present, unexpired allowing 60 seconds of clock skew, and no
    more than an hour after iat, and jti must 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 an isReplay hook called only once every other check has passed;
    the verifier stores nothing and says in replayChecked whether the hook
    ran. A refusal returns a stable reason. verifyProvenance is unchanged.

  • CANONICAL_MAX_DEPTH, CANONICAL_MAX_SAFE_INTEGER,
    INVOCATION_DEFAULT_TTL_SECONDS, INVOCATION_MAX_TTL_SECONDS and
    INVOCATION_CLOCK_SKEW_SECONDS
    , the bounds above as constants.

v0.5.14

Choose a tag to compare

@github-actions github-actions released this 24 Sep 05:02

@truealter/sdk@0.5.14 is staged on npm and reaches installers when a maintainer approves it with 2FA.

npm install @truealter/sdk

Fixed

  • vectors/canonical-jcs-v1.json is in the package. 0.5.13 listed it in
    files and said it shipped, but the published tarball did not carry it, so
    the conformance suite could not be run from a clean install. It can now.

v0.5.13

Choose a tag to compare

@github-actions github-actions released this 24 Sep 02:41

@truealter/sdk@0.5.13 is staged on npm and reaches installers when a maintainer approves it with 2FA.

npm install @truealter/sdk

Fixed

  • signJson and the floor-signing path canonicalise through JCS, RFC 8785.
    The canonical form is now pinned to a vector suite both this package and the
    Python runtime read, vectors/canonical-jcs-v1.json, rather than to either
    side's word for it. Two implementations that disagree about an action's
    canonical form disagree about every binding computed over it, and do so
    silently. The signatures verify on each side and match on neither.

    Reported from outside, by Iman Schrock of EMILIA Protocol against the
    published package on 2026-09-17, after running this package's own tests
    against EMILIA. He found two classes, small decimal values and non-BMP object
    keys hashing differently across the two runtimes. Sweeping the rest of the
    class found four more, and the worst of those was not one of his: an unpaired
    surrogate raised an unhandled encoding error inside the verifier and reached
    the caller as a server fault rather than as a refused signature.

Added

  • vectors/canonical-jcs-v1.json, 16 accept cases and 5 required refusals.
    Ships in the package so the suite is runnable from a clean install. Three of
    the five refusals are exercised on both sides; the other two are unreachable
    in JavaScript, because a parsed value cannot carry a duplicate member name
    and an integer past 2^53 is outside the range RFC 8785 specifies. The Python
    side runs the same file, which is the point of it.

  • toolTier() and toolPrice(), which resolve a tool name the way the
    server does.
    GENERATED_TOOL_TIERS carried only the tools the server's
    own tier dict names, so 21 of the 47 publicly advertised tools were absent
    from it and a raw index of the map returned undefined where the server
    returns free. The map now covers the whole advertised set, and these two
    lookups default an unknown name to free and to no charge, which is what the
    server does at every call site. Indexing the maps directly still works and
    still carries the old hazard for a name neither side knows; read through the
    lookups.

v0.5.11

Choose a tag to compare

@github-actions github-actions released this 03 Sep 06:10

@truealter/sdk@0.5.11 is live on npm with provenance.

npm install @truealter/sdk