Devolutions Gateway Cookbook
February 17, 2026 · View on GitHub
Developer-oriented cookbook for testing purposes.
- RDP routing
- WebSocket-to-TCP tunnel using jetsocat
- Standalone web application custom authentication
- Preflight API
- Network monitoring API
- HTTP/SOCKS proxy configuration
- Proxy-based credentials injection for RDP
- Traffic Audit API
RDP routing
Devolutions Gateway can redirect RDP traffic authorized by a JWT (Json Web Token) signed (JWS) and optionally encrypted (JWE).
The key used to sign must be known by the Devolutions Gateway.
This key is provided through the ProvisionerPublicKeyFile option in the configuration file.
The provisioner can then use its private key to sign a JWT and authorize RDP routing.
Similarly, The key used for token decryption is provided through the DelegationPrivateKeyFile option.
The public counterpart of the delegation key must then be used for token encryption.
JWT structure and claims
Devolutions Gateway is expecting signed claims using JWS (Json Web Signature) as a compact JWT.
Use of RSASSA-PKCS-v1_5 using SHA-256 (RS256) is recommended.
Required claims:
dst_hst(String): target RDP hostjet_cm(String): identity connection mode used for Jet association This must be set tofwd.jet_ap(string): application protocol used over Jet transport. This must be set tordp.exp(Integer): a UNIX timestamp for "expiration"nbf(Integer): a UNIX timestamp for "not before"
This token may be encrypted and wrapped inside another JWT using JWE (Json Web Encryption), in compact form as well.
Use of RSAES OAEP using SHA-256 and MGF1 with SHA-256 (RSA-OAEP-256) and AES GCM using 256-bit key (A256GCM) is
recommended.
Token generation utilities
JWT generation should be facilitated by a provisioner (such as Devolutions Server
or Devolutions Password Hub).
However, you can easily generate a JWT for testing purposes by using CLI tools provided in /tools folder.
tokengen
A native CLI. No binary provided; you will need a Rust toolchain to build yourself. See Install Rust.
$ cargo build --package tokengen --release
The binary is produced inside a target/release folder.
Example:
$ ./tokengen --provisioner-key /path/to/provisioner/private/key.pem forward --dst-hst 192.168.122.70 --jet-ap rdp
Inject token in RDP connection using MSTSC
-
Open MSTSC
-
Enter a JET address in the "computer" field
-
Press the "Save As..." button under the "Connection settings" panel to save ".RDP" file to you PC
-
Open saved ".RDP" file with a text editor
-
Append string "pcb:s:" to the end of the file (e.g: pcb:s:eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOj...)
-
Save file
-
In MSTSC press "Open..." and select your edited file
-
Done. You can start the connection
Inject token in RDP connection using FreeRdp
Using FreeRDP, token can be provided using /pcb argument with xfreerdp.
(e.g: /pcb:eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOj...)
WebSocket-to-TCP tunnel using jetsocat
Our CLI-based toolkit jetsocat can be used to create a network tunnel bridging a WebSocket connection
with a TCP connection. This is useful when debugging the Devolutions Gateway service.
This section describes how to create the following tunnel:
(jetsocat as client) <--WS/TCP/IP--> (Devolutions Gateway) <--TCP/IP--> (jetsocat as server)
Devolutions Gateway service
Configure and start the Devolutions Gateway service (see top-level README.md file).
Server TCP endpoint
Start jetsocat to act as our server endpoint:
cargo run -p jetsocat -- forward tcp-listen://127.0.0.1:9999 -
Received payload will be printed to the standard output.
WebSocket client
Generate a session forwarding token using tokengen (or alternatively, the New-DGatewayToken cmdlet):
cargo run --manifest-path=./tools/tokengen/Cargo.toml --provisioner-key <path/to/provisioner.key> forward --dst-hst 127.0.0.1:9999 --jet-aid 123e4567-e89b-12d3-a456-426614174000
New-DGatewayToken -Type ASSOCIATION -DestinationHost 127.0.0.1:9999 -ApplicationProtocol unknown -AssociationId 123e4567-e89b-12d3-a456-426614174000
Finally, run the following command to connect to the Devolutions Gateway service and open a WebSocket-to-TCP tunnel:
cargo run -p jetsocat -- forward - "ws://127.0.0.1:7171/jet/fwd/tcp/123e4567-e89b-12d3-a456-426614174000?token=<TOKEN>"
Try entering text and see it printed on the other side.
Standalone web application custom authentication
This section demonstrates how to use curl to test the /jet/webapp/app-token and /jet/webapp/session-token endpoints.
The standalone web application must be enabled and configured to use the custom authentication mode.
"WebApp": {
"Enabled": true,
"Authentication": "Custom"
}
A users.txt file is expected as well.
For instance, with a user named David protected by the password abc:
David:$argon2id$v=19$m=16,t=2,p=1$U0tDR3NSSjlBaVJMRmV0Tg\$4KRKy3UsOganH/qTYVvOQg
It’s possible to retrieve a web application token using the POST /jet/webapp/app-token endpoint.
If the Authorization header is absent of the request, the server responds with a challenge:
$ curl -v http://127.0.0.1:7171/jet/webapp/app-token --json '{ "content_type": "WEBAPP", "subject": "David" }'
* Trying 127.0.0.1:7171...
* Connected to 127.0.0.1 (127.0.0.1) port 7171
> POST /jet/webapp/app-token HTTP/1.1
> Host: 127.0.0.1:7171
> User-Agent: curl/8.5.0
> Content-Type: application/json
> Accept: application/json
> Content-Length: 48
>
< HTTP/1.1 401 Unauthorized
< www-authenticate: Basic realm="DGW Custom Auth", charset="UTF-8"
< access-control-allow-origin: *
< vary: origin
< vary: access-control-request-method
< vary: access-control-request-headers
< content-length: 0
< date: Fri, 22 Dec 2023 16:34:12 GMT
<
* Connection #0 to host 127.0.0.1 left intact
Notice the WWW-Authenticate header which advertises the configured authentication mode.
By requesting again with an appropriate Authorization header, a token is returned:
$ curl -v http://127.0.0.1:7171/jet/webapp/app-token --json '{ "content_type": "WEBAPP", "subject": "David" }' -H "Authorization: Basic RGF2aWQ6YWJj"
* Trying 127.0.0.1:7171...
* Connected to 127.0.0.1 (127.0.0.1) port 7171
> POST /jet/webapp/app-token HTTP/1.1
> Host: 127.0.0.1:7171
> User-Agent: curl/8.5.0
> Authorization: Basic RGF2aWQ6YWJj
> Content-Type: application/json
> Accept: application/json
> Content-Length: 48
>
< HTTP/1.1 200 OK
< content-type: text/plain; charset=utf-8
< cache-control: no-cache, no-store
< content-length: 548
< access-control-allow-origin: *
< vary: origin
< vary: access-control-request-method
< vary: access-control-request-headers
< date: Fri, 22 Dec 2023 16:34:46 GMT
<
* Connection #0 to host 127.0.0.1 left intact
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImN0eSI6IldFQkFQUCJ9.eyJqdGkiOiIyODU5NjZhZi04M2VlLTRlNTEtYWYwOS01YWMwZTNjMzQyOTEiLCJpYXQiOjE3MDMyNjI4ODYsIm5iZiI6MTcwMzI2Mjg4NiwiZXhwIjoxNzAzMjkxNjg2LCJzdWIiOiJEYXZpZCJ9.ZO-bbuJpnoOMChbMEHsLj8gIXpcflJQ7DMIS4wo2dgEK4xnCxEJ4AdXVquYnZmGgg7-L1bhgKRi5EM35QFoYrnQDkMfSb6cVROGdp9Lg1-AgGA94Tw8Btq2bWXBJGES67cNFkdN-HJ07ixWKqpRz0wA4yZjn_8Z5B5K_S2_BP7IxfO7ckV_NqQzpaa94oH8XrdX_7dXwG6m-bXkNLOvAzyXHXFQkpb7l9-_CabJ6ZlJpdHcHJ4Tekx1_cHUW7haSyTd1Dp_VWIlnKhaqOcN3BRJ0aW9QaxR7JgSU1k9NWuZL3S5Au_SXUiYrOk2TdNkGDBptImkQhlSim6P4_OXacA
It’s then possible to retrieve a session token:
$ curl -v http://127.0.0.1:7171/jet/webapp/session-token --json '{ "content_type": "ASSOCIATION", "protocol": "rdp", "destination": "tcp://localhost:8888", "lifetime": 60, "session_id": "123e4567-e89b-12d3-a456-426614174000" }' -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImN0eSI6IldFQkFQUCJ9.eyJqdGkiOiIyODU5NjZhZi04M2VlLTRlNTEtYWYwOS01YWMwZTNjMzQyOTEiLCJpYXQiOjE3MDMyNjI4ODYsIm5iZiI6MTcwMzI2Mjg4NiwiZXhwIjoxNzAzMjkxNjg2LCJzdWIiOiJEYXZpZCJ9.ZO-bbuJpnoOMChbMEHsLj8gIXpcflJQ7DMIS4wo2dgEK4xnCxEJ4AdXVquYnZmGgg7-L1bhgKRi5EM35QFoYrnQDkMfSb6cVROGdp9Lg1-AgGA94Tw8Btq2bWXBJGES67cNFkdN-HJ07ixWKqpRz0wA4yZjn_8Z5B5K_S2_BP7IxfO7ckV_NqQzpaa94oH8XrdX_7dXwG6m-bXkNLOvAzyXHXFQkpb7l9-_CabJ6ZlJpdHcHJ4Tekx1_cHUW7haSyTd1Dp_VWIlnKhaqOcN3BRJ0aW9QaxR7JgSU1k9NWuZL3S5Au_SXUiYrOk2TdNkGDBptImkQhlSim6P4_OXacA"
* Trying 127.0.0.1:7171...
* Connected to 127.0.0.1 (127.0.0.1) port 7171
> POST /jet/webapp/session-token HTTP/1.1
> Host: 127.0.0.1:7171
> User-Agent: curl/8.5.0
> Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImN0eSI6IldFQkFQUCJ9.eyJqdGkiOiIyODU5NjZhZi04M2VlLTRlNTEtYWYwOS01YWMwZTNjMzQyOTEiLCJpYXQiOjE3MDMyNjI4ODYsIm5iZiI6MTcwMzI2Mjg4NiwiZXhwIjoxNzAzMjkxNjg2LCJzdWIiOiJEYXZpZCJ9.ZO-bbuJpnoOMChbMEHsLj8gIXpcflJQ7DMIS4wo2dgEK4xnCxEJ4AdXVquYnZmGgg7-L1bhgKRi5EM35QFoYrnQDkMfSb6cVROGdp9Lg1-AgGA94Tw8Btq2bWXBJGES67cNFkdN-HJ07ixWKqpRz0wA4yZjn_8Z5B5K_S2_BP7IxfO7ckV_NqQzpaa94oH8XrdX_7dXwG6m-bXkNLOvAzyXHXFQkpb7l9-_CabJ6ZlJpdHcHJ4Tekx1_cHUW7haSyTd1Dp_VWIlnKhaqOcN3BRJ0aW9QaxR7JgSU1k9NWuZL3S5Au_SXUiYrOk2TdNkGDBptImkQhlSim6P4_OXacA
> Content-Type: application/json
> Accept: application/json
> Content-Length: 161
>
< HTTP/1.1 200 OK
< content-type: text/plain; charset=utf-8
< cache-control: no-cache, no-store
< content-length: 762
< access-control-allow-origin: *
< vary: origin
< vary: access-control-request-method
< vary: access-control-request-headers
< date: Fri, 22 Dec 2023 16:35:51 GMT
<
* Connection #0 to host 127.0.0.1 left intact
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImN0eSI6IkFTU09DSUFUSU9OIn0.eyJkc3RfYWx0IjpbXSwiZHN0X2hzdCI6InRjcDovL2xvY2FsaG9zdDo4ODg4IiwiZXhwIjoxNzAzMjYzMDExLCJpYXQiOjE3MDMyNjI5NTEsImpldF9haWQiOiIxMjNlNDU2Ny1lODliLTEyZDMtYTQ1Ni00MjY2MTQxNzQwMDAiLCJqZXRfYXAiOiJyZHAiLCJqZXRfY20iOiJmd2QiLCJqZXRfZmx0IjpmYWxzZSwiamV0X3JlYyI6ZmFsc2UsImpldF90dGwiOjAsImp0aSI6ImMyZjAzMmU4LWNlZGMtNDk5Zi05ODYyLWExZWFlNjU5NGNiNCIsIm5iZiI6MTcwMzI2Mjk1MX0.WRwnQR-o6UNvIDCiskvOPiQ5XStriaGl4c4UfhZPdZY9hSN4nLajP_inWjbVR8V8h-WcuWZEo_p-s_0Ze6OnEpJ94HRw8e_ANEJ3JWCMrWB7MypWT4V3khPCk-SL29V-if2VUpwPq6Oc9ugpatCxHAJRcUD4FYxr1cy85jU__E3DwOceqGL1OUStfPVw5zqZvJQmZ2ndNO8K_6NhfC2PRSwmMYPPR_vKDeBFShSFQSHCWv2-X3Og5Mjm6R7vyMbvfKY7fs2zRQxwZBoUEaLhEimhqeVcsDH3dF8deN5DbnQ1nq2Eu_eWoJ4y3tBmwaZPMvIDHPq3STZRgehFkY5pqw
Preflight API
Generate a scope token with the scope gateway.preflight or * using tokengen (or alternatively, the New-DGatewayToken cmdlet):
tokengen --provisioner-key <path/to/provisioner.key> scope 'gateway.preflight'
New-DGatewayToken -Type SCOPE -Scope 'gateway.preflight'
Perform preflight operations using curl:
$ curl "127.0.0.1:7171/jet/preflight?token=$(cargo run --manifest-path ./tools/tokengen/Cargo.toml '--' sign --provisioner-key ./config/provisioner.key scope "gateway.preflight")" \
-X POST -H "Content-Type: application/json" \
--data '[
{"id": "a86ae982-e4be-4f84-8ff2-893d66df9bdd", "kind": "get-version"},
{"id": "ef1a3ae9-e55d-48b8-92b0-ae67c29b2e4e", "kind": "provision-token", "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiI1ZTNlODMzZi04NGM3LTQ1NDEtYjY3Ni1hY2MzMjk5ZTM5YjgifQ.1qECGlrW7y9HWFArc6GPHLGTOY7PhAvzKJ5XMRBg4k4"},
{"id": "55821d24-d1df-481c-8b88-66c06f879835", "kind": "resolve-host", "host_to_resolve": "devolutions.net"},
{"id": "8ec4ab6b-39a5-411d-b191-54df2d976820", "kind": "get-running-session-count"},
{"id": "e02d8678-1bc8-4548-b555-548d663ffa1e", "kind": "unexisting-operation"}
]'
And here is how the response may look like:
[
{"operation_id":"e02d8678-1bc8-4548-b555-548d663ffa1e","kind":"alert","alert_status":"unsupported-operation","alert_message":"unsupported operation: unexisting-operation"},
{"operation_id":"a86ae982-e4be-4f84-8ff2-893d66df9bdd","kind":"version","version":"2025.1.4"},
{"operation_id":"ef1a3ae9-e55d-48b8-92b0-ae67c29b2e4e","kind":"ack"}
{"operation_id":"8ec4ab6b-39a5-411d-b191-54df2d976820","kind":"running-session-count","running_session_count":0},
{"operation_id":"55821d24-d1df-481c-8b88-66c06f879835","kind":"resolved-host","resolved_host":"devolutions.net","resolved_addresses":["20.239.34.78"]}
]
Network monitoring API
Basic monitors can be set up to scan servers on an interval. Currently only ping is supported. Managing the configuration and storing the logs is expected to be done by an external server.
Set up a ping monitor for example.com which fires every 10 seconds and times out after 5 seconds.
curl -v http://127.0.0.1:7171/jet/net/monitor/config --json '{"monitors":[{"id":"monitor1","probe":"ping","address":"example.com","interval":10,"timeout":5}]}' -H "Authorization: Bearer $dgwkey"
The monitor will start immediately. Calling this API again will overwrite the configuration, stopping any
monitors no longer present. A body is returned which may contain a list of monitors that could not be started
due their type (set in the field probe) being unsupported.
Retrieve the logs:
curl -v http://127.0.0.1:7171/jet/net/monitor/log/drain -X POST -H "Authorization: Bearer $dgwkey"
The response will look similar to this:
{"entries":[{"monitor_id":"monitor1","request_start_time":"2025-08-22T17:07:34.3370521Z","response_success":true,"response_time":0.0585181}]}
Each log entry is only returned once. After you make this request, the existing log is deleted from memory.
HTTP/SOCKS proxy configuration
Devolutions Gateway and Devolutions Agent support HTTP/HTTPS/SOCKS proxy configuration for outbound requests. Proxies are used for requests such as:
- Subscriber API notifications (Gateway)
- AI Gateway provider requests (Gateway)
- Update downloads and version checks (Agent)
The proxy configuration supports three modes:
- Off: Never use a proxy, ignore environment variables
- System: Auto-detect proxy from environment variables or system settings (default)
- Manual: Use explicitly configured proxy URLs
System proxy detection (default)
By default (Mode: System), proxy configuration is automatically detected from system settings:
- Environment variables: HTTP_PROXY, HTTPS_PROXY, ALL_PROXY, NO_PROXY
- Linux sysconfig:
/etc/sysconfig/proxyon RHEL/SUSE systems - Windows Registry: Per-user and machine-wide settings with WinHTTP fallback
- macOS System Configuration: SCDynamicStoreCopyProxies()
Gateway configuration example:
{
"Proxy": {
"Mode": "System"
}
}
Agent configuration example:
{
"Proxy": {
"Mode": "System"
}
}
Since System is the default mode, you can omit the Proxy section entirely to use system proxy detection.
Manual proxy configuration
You can override auto-detection by setting Mode: Manual and specifying explicit proxy URLs.
The configuration supports protocol-specific proxies (Http, Https) and a fallback proxy (All).
Protocol-specific proxies:
{
"Proxy": {
"Mode": "Manual",
"Http": "http://proxy.corp:8080",
"Https": "http://proxy.corp:8443"
}
}
SOCKS5 proxy as fallback for all protocols:
{
"Proxy": {
"Mode": "Manual",
"All": "socks5://proxy.corp:1080"
}
}
Corporate proxy with authentication:
{
"Proxy": {
"Mode": "Manual",
"Http": "http://username:password@proxy.corp:8080",
"Https": "http://username:password@proxy.corp:8080"
}
}
Combined configuration with exclude list:
{
"Proxy": {
"Mode": "Manual",
"Http": "http://proxy.corp:8080",
"Https": "http://proxy.corp:8080",
"Exclude": ["localhost", ".internal.net", "192.168.1.0/24"]
}
}
Disabling proxy
To completely disable proxy usage (ignoring system settings):
{
"Proxy": {
"Mode": "Off"
}
}
Exclusions
When using Mode: Manual, specify exclusions in the Exclude array:
{
"Proxy": {
"Mode": "Manual",
"Http": "http://proxy.corp:8080",
"Exclude": [
"localhost",
".internal.corp",
"devolutions.net",
"127.0.0.1",
"192.168.0.0/16"
]
}
}
The Exclude list supports NO_PROXY semantics:
- Wildcard:
*(bypass proxy for all targets) - Exact hostname:
localhost,example.com - Domain suffix:
.corp.local(matchesfoo.corp.local) - IP address:
127.0.0.1 - CIDR range:
10.0.0.0/8,192.168.0.0/16
Proxy selection priority
For Mode: Manual, the proxy is selected based on the target URL scheme:
- For
http://requests → useHttpproxy, fallback toAll - For
https://requests → useHttpsproxy, fallback toAll - For other schemes → use
Allproxy
If the target matches an entry in the Exclude list, no proxy is used.
Testing proxy configuration
Test Gateway subscriber notifications with a proxy:
- Configure the proxy in
gateway.json:
{
"Proxy": {
"Mode": "Manual",
"Https": "http://proxy.corp:8080"
},
"Subscriber": {
"Url": "https://example.com/notifications",
"Token": "your-token-here"
}
}
- Monitor Gateway logs for proxy-related messages:
tail -f /var/log/devolutions-gateway/gateway.log | grep -i proxy
Test Agent updates with a proxy:
- Configure the proxy in
agent.json:
{
"Proxy": {
"Mode": "Manual",
"Https": "http://proxy.corp:8080"
},
"Updater": {
"Enabled": true
}
}
- Trigger an update check and monitor logs:
# View agent logs
tail -f /var/log/devolutions-agent/agent.log | grep -i "download\|proxy"
Proxy-based credentials injection for RDP
How it works
- Perform two-way forwarding between the client and the target until the TLS security upgrade.
- Separately perform the TLS upgrade for both the client and the server, effectively acting as a man-in-the-middle.
- The client must trust the TLS certificate configured in the Devolutions Gateway.
- Separately perform CredSSP authentication as server with the client, and as client with the target.
- The fake, proxy credentials are used with the client.
- The real, target credentials are used with the target.
- Proceed with the usual two-way forwarding (except we can actually see and inspect all the traffic)
Prerequisites
- Generate some tokens. You can use
tokengenor the PowerShell cmdlet.- Generate a session token for the RDP session.
- Generate a scope token for the preflight API.
- Configure the TLS certificate and private key.
Optionally, configure
CredSspCertificateFileandCredSspPrivateKeyFileto use a different certificate for CredSSP. - Run the Devolutions Gateway.
- We’ll assume it runs on localhost, and it listens for HTTP on 7171 and TCP on 8181.
- Adjust to your needs.
Push the credentials
curl "127.0.0.1:7171/jet/preflight?token=<SCOPE_TOKEN>" \
-X POST -H "Content-Type: application/json" \
--data '[
{"id": "ef1a3ae9-e55d-48b8-92b0-ae67c29b2e4e", "kind": "provision-credentials", "token": "<SESSION_TOKEN>",
"proxy_credential": { "kind": "username-password", "username": "FakeUser", "password": "FakePassword" },
"target_credential": { "kind": "username-password", "username": "RealUser", "password": "RealPassword" } }
]'
Connect using the fake (proxy) credentials
xfreerdp3 /v:127.0.0.1:8181 /u:'FakeUser' /p:'FakePassword' /pcb:<SESSION_TOKEN>
You may also add the option /cert:ignore if the certificate you configured is not trusted.
Demo
proxy-based-credentials-injection-prototype.webm
Traffic Audit API
The Traffic Audit API provides endpoints to claim and acknowledge traffic events for external processing. This enables integration with external auditing and compliance systems.
Overview
The traffic audit system uses a lease-based claim/acknowledgment pattern:
- Claim events with a lease duration to prevent concurrent processing
- Process the events in your external system
- Acknowledge the events to remove them from the queue
Authentication
Both endpoints require a SCOPE token with appropriate scopes:
/jet/traffic/claimrequiresgateway.traffic.claimor*scope/jet/traffic/ackrequiresgateway.traffic.ackor*scope
Use tokengen:
tokengen --provisioner-key <path/to/provisioner.key> scope 'gateway.traffic.claim'
tokengen --provisioner-key <path/to/provisioner.key> scope 'gateway.traffic.ack'
Or alternatively, the New-DGatewayToken cmdlet:
New-DGatewayToken -Type SCOPE -Scope 'gateway.traffic.claim'
New-DGatewayToken -Type SCOPE -Scope 'gateway.traffic.ack'
Claiming Traffic Events
Claim up to a specified number of traffic events with a lease duration.
# Basic claim request (uses defaults: lease_ms=300000, max=100)
curl -X POST "https://gateway.example.com/jet/traffic/claim" \
-H "Authorization: Bearer <TOKEN>"
# Claim with custom parameters
curl -X POST "https://gateway.example.com/jet/traffic/claim?lease_ms=60000&max=50" \
-H "Authorization: Bearer <TOKEN>"
Query Parameters:
lease_ms(optional): Lease duration in milliseconds (1000-3600000, default: 300000 = 5 minutes)max(optional): Maximum number of events to claim (1-1000, default: 100)
Response:
[
{
"id": "01JFQH8V5HCZN8XKZJ3Y6M4W9P",
"session_id": "550e8400-e29b-41d4-a716-446655440000",
"outcome": "normal_termination",
"protocol": "tcp",
"target_host": "example.com",
"target_ip": "192.168.1.100",
"target_port": 80,
"connect_at_ms": 1672531200000,
"disconnect_at_ms": 1672531260000,
"active_duration_ms": 60000,
"bytes_tx": 1024,
"bytes_rx": 2048
}
]
Acknowledging Traffic Events
Acknowledge processed events to remove them from the queue.
# Acknowledge specific event IDs (ULID format)
curl -X POST "https://gateway.example.com/jet/traffic/ack" \
-H "Authorization: Bearer <TOKEN>" \
--json '{"ids": ["01JFQH8V5HCZN8XKZJ3Y6M4W9P", "01JFQH8V5JD2N7XKZJ4Y7M5W0Q", "01JFQH8V5KE3N9XKZJ5Y8M6W1R"]}'
Response:
{
"deleted_count": 3
}
Notes
- Event IDs are ULIDs (Universally Unique Lexicographically Sortable Identifiers)
- Events are returned in ascending ID order (ULIDs are time-ordered)
- Claimed events are locked for the specified lease duration
- If not acknowledged before lease expiry, events become available for reclaiming
- Events are permanently deleted after acknowledgment (no retention)