Skip to content

fix(licensing): validate each token-2 once per endpoint - #20

Merged
davehorton merged 1 commit into
mainfrom
fix/validate-token-2-once
Oct 2, 2026
Merged

davehorton merged 1 commit into
mainfrom
fix/validate-token-2-once

Conversation

@davehorton

Copy link
Copy Markdown
Contributor

Problem

token-2 is a one-time proof, minted at call setup, that an outbound call left through a licensed SBC. Endpoint.set('jambonz_session_token_2', …) validates it via licensing.validate-token-2 every time it's set, and the feature-server (lib/utils/place-outdial.js) sets it on every provisional response and again after the final response. Later responses carry the same token with its original timestamp, and mediajam rejects tokens older than 40 s (internal/license/token.go, tokenTTLSeconds).

So on any outbound call that rings for more than 40 seconds, a re-sent 180/183 fails as session token invalid: expired, and this module destroys the endpoint (license-violation) before the call is answered. If the callee then answers, the call runs with no media until the media timeout.

Seen on a production jambonz mini (mediajam 0.5.8, feature-server 11.1.5) on 2026-09-30: 42 calls, every failure 40.3–59.1 s after the INVITE (median 50.0 s). 36 were never answered. 6 were answered after the failure: the agent ran, but the caller was never heard, and the call ended after about 38 s of silence.

Fix

_validateSessionToken2 remembers the token-2 that validated and skips re-validating the same token. The first check happens on the first provisional, while the token is seconds old, so the anti-replay window is unchanged.

  • A different token-2 is still validated (and torn down if invalid).
  • A token that failed validation is not remembered.
  • Minting a new token-1 clears the remembered token-2.

Raising the 40 s TTL instead was considered and rejected: it only moves the cliff (carriers ring 60–120 s) and widens the replay window.

Tests

Four new tests in test/licensing-conduit.test.js, using a validator that accepts a token once and then rejects it as expired, like mediajam after 40 s. The long-ring test fails without the fix. npm test: 105 pass, 0 fail (8 skipped). Lint clean.

Follow-up

After release, bump @jambonz/mrf in feature-server (main and the 11.1.5 customer branch).

🤖 Generated with Claude Code

token-2 is a one-time proof, minted at call setup, that an outbound call
egressed through a licensed SBC. The feature-server sets it on every
provisional response and again after the final response, and later
responses carry the same token with its original timestamp. mediajam
rejects a token older than 40s, so on any outbound call that rang that
long, a re-sent 180/183 failed validation as 'expired' and the endpoint
was destroyed (license-violation) before answer. Answered calls then ran
with no media until the media timeout.

Remember the token-2 that validated and skip re-validating the same
token. A different token-2 is still validated; a failed one is not
remembered; minting a new token-1 clears it.

Seen on a production mini: 42 calls on 2026-09-30, all failing 40-59s
after the INVITE; 6 of them answered with the caller never heard.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@davehorton
davehorton merged commit 625c873 into main Oct 2, 2026
5 checks passed
@davehorton
davehorton deleted the fix/validate-token-2-once branch October 2, 2026 13:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant