NetProbe¶
Live network debugging: packet capture (tcpdump), ping, traceroute.
All endpoints below are mounted under /api. Authentication: Authorization: Bearer <token> unless noted as public.
Endpoint count: 14
Endpoints¶
| Method | Path | Handler | Guards |
|---|---|---|---|
GET |
/api/netprobe/capture/:id/packets |
netprobeCapturePackets |
RequireSuperuser(), RequirePlugin(db, "netprobe") |
GET |
/api/netprobe/capture/:id/status |
netprobeCaptureStatus |
RequireSuperuser(), RequirePlugin(db, "netprobe") |
POST |
/api/netprobe/capture/:id/stop |
netprobeStopCapture |
RequireSuperuser(), RequirePlugin(db, "netprobe") |
POST |
/api/netprobe/capture/start |
netprobeStartCapture |
RequireSuperuser(), RequirePlugin(db, "netprobe") |
DELETE |
/api/netprobe/history/:field |
netprobeClearHistory |
RequireSuperuser(), RequirePlugin(db, "netprobe") |
GET |
/api/netprobe/history/:field |
netprobeGetHistory |
RequireSuperuser(), RequirePlugin(db, "netprobe") |
POST |
/api/netprobe/history/:field |
netprobeSaveHistory |
RequireSuperuser(), RequirePlugin(db, "netprobe") |
POST |
/api/netprobe/httping |
netprobeHttping |
RequireSuperuser(), RequirePlugin(db, "netprobe") |
POST |
/api/netprobe/httping/install |
netprobeHttpingInstall |
RequireSuperuser(), RequirePlugin(db, "netprobe") |
GET |
/api/netprobe/httping/status |
netprobeHttpingStatus |
RequireSuperuser(), RequirePlugin(db, "netprobe") |
GET |
/api/netprobe/interfaces |
netprobeListInterfaces |
RequireSuperuser(), RequirePlugin(db, "netprobe") |
POST |
/api/netprobe/ping |
netprobePing |
RequireSuperuser(), RequirePlugin(db, "netprobe") |
POST |
/api/netprobe/portcheck |
netprobePortCheck |
RequireSuperuser(), RequirePlugin(db, "netprobe") |
POST |
/api/netprobe/traceroute |
netprobeTraceroute |
RequireSuperuser(), RequirePlugin(db, "netprobe") |
Source¶
Handlers for this section live in api/routes/handlers_*.go in the StarkOne source. Each handler binds its request body with gin.ShouldBindJSON(&req) and responds with the standard envelope:
On error:
Reference¶
GET /api/netprobe/capture/:id/packets¶
Returns the list of parsed packets captured so far by the running (or finished) capture session identified by :id.
Guards: RequireSuperuser(), RequirePlugin(db, "netprobe")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
string (UUID) | The capture session ID returned by POST /api/netprobe/capture/start. Must match the single active session. |
Response - 200 OK
The data field is a flat array of parsed packet objects (not an object). Each packet has the shape produced by parseTcpdumpLine.
{
"data": [
{
"timestamp": "12:00:00.123456",
"src_ip": "203.0.113.10",
"dst_ip": "203.0.113.20",
"src_port": "443",
"dst_port": "51514",
"protocol": "TCP",
"length": 60,
"info": "Flags [P.], seq 1:2, ack 1, win 502, length 60",
"raw_line": "12:00:00.123456 IP 203.0.113.10.443 > 203.0.113.20.51514: Flags [P.], length 60"
}
]
}
Errors
| Status | Code | Meaning |
|---|---|---|
404 |
not_found |
No active capture session, or :id does not match the active session. |
Example
curl https://panel.example.com:2087/api/netprobe/capture/4d2c.../packets \
-H "Authorization: Bearer $TOKEN"
GET /api/netprobe/capture/:id/status¶
Returns the status and packet count of the capture session identified by :id.
Guards: RequireSuperuser(), RequirePlugin(db, "netprobe")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
string (UUID) | The capture session ID. Must match the single active session. |
Response - 200 OK
{
"data": {
"id": "4d2c...",
"status": "running",
"error": "",
"packet_count": 42,
"max_count": 100
}
}
status is one of running, done, or error.
Errors
| Status | Code | Meaning |
|---|---|---|
404 |
not_found |
No active capture session, or :id does not match the active session. |
Example
curl https://panel.example.com:2087/api/netprobe/capture/4d2c.../status \
-H "Authorization: Bearer $TOKEN"
POST /api/netprobe/capture/:id/stop¶
Stops the running capture session identified by :id (kills the underlying tcpdump process and cancels the auto-stop timer).
Guards: RequireSuperuser(), RequirePlugin(db, "netprobe")
Path parameters
| Name | Type | Description |
|---|---|---|
id |
string (UUID) | The capture session ID. Must match the single active session. |
Response - 200 OK
Errors
| Status | Code | Meaning |
|---|---|---|
404 |
not_found |
No active capture session, or :id does not match the active session. |
Side effects
- If the session is still
running, kills the underlyingtcpdumpprocess and stops the 60-second auto-stop timer, then marks the sessiondone.
Example
curl -X POST https://panel.example.com:2087/api/netprobe/capture/4d2c.../stop \
-H "Authorization: Bearer $TOKEN"
POST /api/netprobe/capture/start¶
Starts a tcpdump packet capture on the given interface (with an optional BPF filter) and returns a session ID. Only one capture may run at a time.
Guards: RequireSuperuser(), RequirePlugin(db, "netprobe")
Request body
{
"interface": "string (required)",
"filter": "string (optional, BPF filter)",
"count": "int (optional)"
}
| Field | Type | Required | Description |
|---|---|---|---|
interface |
string | yes | Network interface to capture on (e.g. eth0). Rejected if empty. |
filter |
string | no | BPF filter expression; split on whitespace and passed as separate tcpdump args. |
count |
int | no | Max packets to capture. Defaults to 100 when <= 0; clamped to 1000 if larger. |
Response - 200 OK
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
Invalid JSON body, or interface is empty. |
409 |
capture_running |
A capture is already running; stop it first. |
500 |
pipe_error |
Failed to open the tcpdump stdout pipe. |
500 |
start_error |
Failed to start the tcpdump process. |
Side effects
- Executes
tcpdump -nn -l --immediate-mode -i <interface> -c <count> [filter...]as a background process. - Stores a single global active capture session in memory and streams parsed packets into it via a goroutine.
- Schedules a 60-second auto-stop timer that kills the process and marks the session
done.
Example
curl -X POST https://panel.example.com:2087/api/netprobe/capture/start \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"interface": "eth0", "filter": "tcp port 443", "count": 200}'
DELETE /api/netprobe/history/:field¶
Clears all stored autocomplete history entries for the given field type.
Guards: RequireSuperuser(), RequirePlugin(db, "netprobe")
Path parameters
| Name | Type | Description |
|---|---|---|
field |
string | History field type. Must be one of filter, ping, or trace. |
Response - 200 OK
This handler returns a flat (non-enveloped) body.
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
- | Invalid field type. Body is { "error": "invalid field type" } (flat, no code envelope). |
Side effects
- Deletes all rows from the
netprobe_historytable for the matchingfield_type.
Example
curl -X DELETE https://panel.example.com:2087/api/netprobe/history/ping \
-H "Authorization: Bearer $TOKEN"
GET /api/netprobe/history/:field¶
Returns the 10 most recently used autocomplete values for the given field type.
Guards: RequireSuperuser(), RequirePlugin(db, "netprobe")
Path parameters
| Name | Type | Description |
|---|---|---|
field |
string | History field type. Must be one of filter, ping, or trace. |
Response - 200 OK
The data field is a flat array of strings (newest first), at most 10 entries.
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
- | Invalid field type. Body is { "error": "invalid field type" } (flat). |
500 |
- | Database query failed. Body is { "error": "<message>" } (flat). |
Example
POST /api/netprobe/history/:field¶
Saves a value into the autocomplete history for the given field type, moving it to the top and trimming the list to 10 entries.
Guards: RequireSuperuser(), RequirePlugin(db, "netprobe")
Path parameters
| Name | Type | Description |
|---|---|---|
field |
string | History field type. Must be one of filter, ping, or trace. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
value |
string | yes | The value to store. Trimmed of surrounding whitespace; must be non-empty. |
Response - 200 OK
This handler returns a flat (non-enveloped) body.
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
- | Invalid field type. Body is { "error": "invalid field type" } (flat). |
400 |
- | Invalid JSON body, or value is empty after trimming. Body is { "error": "value required" } (flat). |
Side effects
- Deletes any existing
netprobe_historyrow with the samefield_typeandvalue(so the entry moves to the top). - Inserts a new
netprobe_historyrow. - Trims
netprobe_historyfor thatfield_typedown to the 10 most recent entries.
Example
curl -X POST https://panel.example.com:2087/api/netprobe/history/ping \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"value": "example.com"}'
POST /api/netprobe/httping¶
Runs httping against a host or URL and returns the raw output lines plus parsed round-trip statistics.
Guards: RequireSuperuser(), RequirePlugin(db, "netprobe")
Request body
| Field | Type | Required | Description |
|---|---|---|---|
host |
string | yes | Hostname or URL to probe. If it starts with http, it is passed via -g <url>; otherwise passed as a bare host. Rejected if it contains shell metacharacters. |
count |
int | no | Number of probes. Defaults to 4 when <= 0; clamped to 50 if larger. |
Response - 200 OK
{
"data": {
"lines": ["connected to 203.0.113.10:80 ...", "round-trip min/avg/max = 1.2/2.3/3.4 ms"],
"stats": {
"min_ms": "1.2",
"avg_ms": "2.3",
"max_ms": "3.4",
"loss_pct": "0"
}
}
}
stats values are strings; loss_pct defaults to "100" if no loss line is parsed.
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
Invalid JSON body, or host is empty. |
400 |
bad_request |
host contains disallowed shell metacharacters. |
Side effects
- Executes
httping -c <count> -t 3 [-g] <host>and captures its combined output. (Output is parsed; the command's exit error is ignored.)
Example
curl -X POST https://panel.example.com:2087/api/netprobe/httping \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"host": "https://example.com", "count": 5}'
POST /api/netprobe/httping/install¶
Installs the httping package using the system package manager, then verifies the binary is on PATH.
Guards: RequireSuperuser(), RequirePlugin(db, "netprobe")
Response - 200 OK
Errors
| Status | Code | Meaning |
|---|---|---|
500 |
install_failed |
httping is still not on PATH after the install attempt. |
Side effects
- If
/etc/redhat-releaseexists, runsyum install -y httping; otherwise runsapt-get install -y httping. - Installs a system package as root.
Example
curl -X POST https://panel.example.com:2087/api/netprobe/httping/install \
-H "Authorization: Bearer $TOKEN"
GET /api/netprobe/httping/status¶
Reports whether the httping binary is available on PATH.
Guards: RequireSuperuser(), RequirePlugin(db, "netprobe")
Response - 200 OK
installed is true when exec.LookPath("httping") succeeds.
Example
GET /api/netprobe/interfaces¶
Lists the network interface names available on the host.
Guards: RequireSuperuser(), RequirePlugin(db, "netprobe")
Response - 200 OK
The data field is a flat array of interface name strings (empty array if none / on error).
Side effects
- Executes
ip -o link showto enumerate interfaces (read-only).
Example
POST /api/netprobe/ping¶
Runs ping against a host and returns parsed per-reply data, summary statistics, and the raw output.
Guards: RequireSuperuser(), RequirePlugin(db, "netprobe")
Request body
{
"host": "string (required)",
"count": "int (optional)",
"size": "int (optional)",
"interval": "float (optional)"
}
| Field | Type | Required | Description |
|---|---|---|---|
host |
string | yes | Host to ping. Rejected if empty or if it contains shell metacharacters. |
count |
int | no | Number of echo requests. Defaults to 4 when <= 0; clamped to 50 if larger. |
size |
int | no | Payload size in bytes. Defaults to 64 when <= 0; clamped to 65500 if larger. |
interval |
float | no | Seconds between probes. Defaults to 1.0 when <= 0; floored to 0.2 if smaller. |
Response - 200 OK
{
"data": {
"packets": [
{ "seq": 1, "ttl": 64, "time_ms": 0.5, "size": 64 }
],
"stats": {
"sent": 4,
"received": 4,
"loss_pct": 0.0,
"min_ms": 0.1,
"avg_ms": 0.2,
"max_ms": 0.3
},
"raw": "PING example.com ...\n"
}
}
If parsing finds no replies, stats defaults to sent: 0, received: 0, loss_pct: 100.0 and the rtt fields 0.0.
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
Invalid JSON body, or host is empty. |
400 |
bad_request |
host contains disallowed shell metacharacters. |
Side effects
- Executes
ping -c <count> -s <size> -i <interval> <host>. The process is killed if it runs longer thancount*interval + 10seconds.
Example
curl -X POST https://panel.example.com:2087/api/netprobe/ping \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"host": "example.com", "count": 4, "size": 64, "interval": 1.0}'
POST /api/netprobe/portcheck¶
Checks whether a TCP or UDP port on a host is reachable and reports the connection time.
Guards: RequireSuperuser(), RequirePlugin(db, "netprobe")
Request body
| Field | Type | Required | Description |
|---|---|---|---|
host |
string | yes | Target host. Rejected if empty or if it contains shell metacharacters. |
port |
int | yes | Target port; must be > 0. Clamped to 65535 if larger. |
protocol |
string | no | tcp (default) or udp. Any value other than udp is treated as TCP. |
Response - 200 OK
Always returns 200 (including for unreachable/closed ports); the open field carries the result.
{
"data": {
"open": true,
"message": "TCP connection to 203.0.113.10:443 successful",
"time_ms": "12.3"
}
}
For a failed/closed port, open is false, message describes the failure, and time_ms is an empty string. For UDP, a read timeout (no ICMP unreachable) is reported as open: true.
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
Invalid JSON body, or host empty / port <= 0. |
400 |
bad_request |
host contains disallowed shell metacharacters. |
Side effects
- Opens an outbound TCP or UDP connection (3-second timeout) to
host:port. For UDP it also writes a single probe byte and waits for a response. No shell commands are executed.
Example
curl -X POST https://panel.example.com:2087/api/netprobe/portcheck \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"host": "203.0.113.10", "port": 443, "protocol": "tcp"}'
POST /api/netprobe/traceroute¶
Runs traceroute (ICMP mode) against a host and returns parsed per-hop data and the raw output.
Guards: RequireSuperuser(), RequirePlugin(db, "netprobe")
Request body
| Field | Type | Required | Description |
|---|---|---|---|
host |
string | yes | Host to trace to. Rejected if empty or if it contains shell metacharacters. |
max_hops |
int | no | Maximum hop count. Defaults to 30 when <= 0; clamped to 64 if larger. |
Response - 200 OK
{
"data": {
"hops": [
{
"hop_number": 1,
"ip": "203.0.113.1",
"hostname": "",
"rtt_ms": 0.4,
"loss_pct": 0.0
}
],
"raw": "traceroute to example.com ...\n"
}
}
A fully timed-out hop is reported with ip: "*" and loss_pct: 100.0. The hostname field is always present but is left empty by the current parser.
Errors
| Status | Code | Meaning |
|---|---|---|
400 |
bad_request |
Invalid JSON body, or host is empty. |
400 |
bad_request |
host contains disallowed shell metacharacters. |
Side effects
- Executes
traceroute -I -n -m <max_hops> <host>. The process is killed if it runs longer thanmax(max_hops*6, 60)seconds.
Example
curl -X POST https://panel.example.com:2087/api/netprobe/traceroute \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"host": "example.com", "max_hops": 30}'