Skip to content

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:

{ "data": { ... } }

On error:

{ "error": { "code": "string", "message": "string" } }

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

{ "data": { "success": true } }

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 underlying tcpdump process and stops the 60-second auto-stop timer, then marks the session done.

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

{ "data": { "session_id": "4d2c..." } }

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.

{ "ok": true }

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_history table for the matching field_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.

{ "data": ["203.0.113.10", "example.com"] }

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

curl https://panel.example.com:2087/api/netprobe/history/ping \
  -H "Authorization: Bearer $TOKEN"

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

{ "value": "string (required)" }
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.

{ "ok": true }

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_history row with the same field_type and value (so the entry moves to the top).
  • Inserts a new netprobe_history row.
  • Trims netprobe_history for that field_type down 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

{
  "host": "string (required)",
  "count": "int (optional)"
}
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

{ "data": { "installed": true } }

Errors

Status Code Meaning
500 install_failed httping is still not on PATH after the install attempt.

Side effects

  • If /etc/redhat-release exists, runs yum install -y httping; otherwise runs apt-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

{ "data": { "installed": true } }

installed is true when exec.LookPath("httping") succeeds.

Example

curl https://panel.example.com:2087/api/netprobe/httping/status \
  -H "Authorization: Bearer $TOKEN"

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).

{ "data": ["lo", "eth0"] }

Side effects

  • Executes ip -o link show to enumerate interfaces (read-only).

Example

curl https://panel.example.com:2087/api/netprobe/interfaces \
  -H "Authorization: Bearer $TOKEN"

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 than count*interval + 10 seconds.

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

{
  "host": "string (required)",
  "port": "int (required)",
  "protocol": "string (optional)"
}
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

{
  "host": "string (required)",
  "max_hops": "int (optional)"
}
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 than max(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}'