Docs Send sipnab's vCons to vcon.store
Send sipnab's vCons to vcon.store
Forward sipnab's vCons to the hosted vcon.store with sipnab --vcon-forward: the choice of whether to --redact them first, which the store refuses today, the compat mode its validator needs, and what the store's signature and consent fields do and do not mean.
On this page
vcon.store is a hosted store for
vCons. This page sends the containers sipnab writes to it
with sipnab --vcon-forward, the forwarder that
Deliver the spool to a store
describes.
Status on 2026-10-07: sipnab’s containers work only through a compat
mode, and only when they carry audio. vcon.store refuses extensions in the
form both vCon drafts define, so a sipnab container sent unchanged draws 400.
--vcon-forward-compat vcon-store sends a copy the store accepts, and leaves
the container on disk unchanged. Even with it, the store refuses a container
whose Dialog Object has no type, which is what sipnab writes for a call whose
container carries no audio: the run kept none, or --redact withheld it. The measurements are the evidence.
Before you send anything
A capture carries personal data: telephone numbers, addresses, the headers
your platform sets, and, with --retain-audio, the call audio. vcon.store is
another party’s system. Decide what may leave the machine first:
--redactreplaces identities, addresses and correlation identifiers with keyed tokens, and withholds the audio. It works on a capture file (-I), not on a live capture.- A redacted container carries no audio, so its Dialog Object has no
type, and vcon.store refuses it. The compat mode refuses it before sending, with that reason. Today the only sipnab container vcon.store accepts is one exported without--redactand with audio: the call audio and the identifiers the signaling carried, as captured. - What may you conclude lists what a container carries.
1. Write the containers
Export the calls from a capture file with their audio. --retain-audio makes
sipnab type each call’s Dialog Object recording, with the parties, which the
store requires. This container is not redacted: it carries the audio and the
identifiers as captured.
sipnab -N --no-cli-print -I calls.pcap --retain-audio \
--export-vcon-when "state == 'Completed'" --export-vcon-dir spool
With --redact added, the same command writes containers without audio, and
step 3 moves each one to spool/failed with the reason.
sipnab prints Wrote N vCon container(s) to 'spool'.
2. Put the token in a file
vcon.store authenticates with a bearer token. Put it in a file only you can read. The forwarder refuses one that other users can read:
# Run all of these, in order.
umask 077
printf 'Authorization: Bearer %s\n' "$VCON_STORE_TOKEN" > vcon-store.auth
$VCON_STORE_TOKEN is the token vcon.store issued you. The forwarder never
prints it, and removes it from any answer it keeps.
3. Send them
# Run all of these, in order.
sipnab --vcon-forward spool --vcon-forward-url https://api.vcon.store/v1/vcons \
--vcon-forward-auth-file vcon-store.auth --vcon-forward-compat vcon-store \
--vcon-forward-once
echo "exit $?"
For each container the forwarder logs one line naming the change it made to
the copy it sent, then a delivered line with the store’s status, and moves
the file to spool/delivered. The exit status is 0 when the store accepted
every container. A container the store or the compat mode refused is in
spool/failed, beside a .error.json record that says why. A 401 or 403
is different: it refuses the token or the client for every container, so the
forwarder stops, moves nothing, logs the status and the start of the answer,
and exits 3.
These commands ran on 2026-10-07 against a local stand-in that answers as
the maintainer measured vcon.store answering, not against vcon.store itself. Without
--vcon-forward-compat vcon-store the stand-in answered 400 extensions: Expected object, received array, as the store does, and the forwarder moved
the container to spool/failed.
What the compat mode changes, and why
--vcon-forward-compat vcon-store changes the copy the forwarder SENDS. The
file in the spool, and the copy in spool/delivered, keep the form the drafts
define. The forwarder logs each change, one line per container.
| In the container | Sent to vcon.store | Why |
|---|---|---|
"extensions": ["sip-signaling", "CC"] | "extensions": {"sip-signaling": true, "CC": true} | Section 4.1.3 of both draft-ietf-vcon-vcon-core-02 and -03 defines extensions as an array of strings. vcon.store refuses that and accepts an object. The copy keeps every name, in its order. |
a Dialog Object with no type or no parties | nothing: the container is not sent | vcon.store requires both on every Dialog Object, as draft-ietf-vcon-vcon-core-02 did. sipnab writes a Dialog Object with neither when the container carries no audio, which section 4.3 of draft-ietf-vcon-vcon-core-03 allows. The forwarder does not invent a type sipnab did not observe, and does not drop the object, which would leave other indexes pointing at nothing. It moves the container to spool/failed with a reason that names --retain-audio and --redact. |
The forwarder sends every other byte of the container as sipnab wrote it. You can
drop the mode once vcon.store accepts extensions as the array of strings the drafts
define.
The measurements
The maintainer measured these against vcon.store’s API on 2026-10-07. This page’s commands did not contact the store.
| Sent | vcon.store answered |
|---|---|
POST /v1/vcons with "extensions": ["sip-signaling", "CC"] | 400, extensions: Expected object, received array |
the same with extensions as an object of names mapped to true | 201, stored as sent |
a vCon with no audio, whose Dialog Object is {} | 400: the store requires type and parties |
the same with extensions removed | 201 |
GET of a stored vCon | the content unchanged, inside a _meta envelope |
DELETE of a stored vCon | 200, and a GET after it 404 |
a request with Python’s default HTTP User-Agent | 403, error code: 1010, from the Cloudflare front. The front accepted a curl User-Agent. The forwarder sends User-Agent: sipnab/<version> |
Not measured: whether vcon.store answers 409 for a uuid it already holds, or
accepts a PUT to /v1/vcons/{uuid}. --vcon-forward-replace-url exists for a
store that does, and this page does not use it. Also not measured: whether the
store accepts a recording Dialog Object with no body, which is what step 1
writes under --redact.
What the store adds, and what it does not mean
On each create the store wrote one entry to its transparency log (SCITT): the
event vcon_created, a SHA-256 hash of the payload, a COSE signed statement
and a receipt. Each stored vCon also comes back with a _meta envelope that
says form: unsigned, consentStatus: unknown and retentionAction: redact.
- The signature is the store’s, not sipnab’s. It says what vcon.store received. The container it covers is an observation from a tap, which sipnab never signs, so the signature does not make the container a recording.
consentStatus: unknownis accurate. sipnab records no consent, and a container carries none.
When something does not work
- Every container lands in
spool/failedwith status400andextensions: Expected object, received array. Add--vcon-forward-compat vcon-store, move the files fromspool/failedback tospool, and run the forwarder again. - A container lands in
spool/failedwith a reason abouttypeandparties. The container carries no audio: the run kept none for that call, or the export ran with--redact. Export it again with--retain-audioand without--redact. - The forwarder exits
3and logs403witherror code: 1010. The Cloudflare front refused the request’s client. The forwarder sendsUser-Agent: sipnab/<version>. Check that nothing between it and the store replaces that header. It moved no file, so run it again once you fix that. - The forwarder exits
3and logs401. The token invcon-store.authis wrong or expired. Every container is still inspool.