portwho --json (or -j) writes one JSON document to standard output instead of text. It combines with every filter and always wins over the table and details views, so portwho 8080 --json is a JSON answer for port 8080.
The contract
- The document carries
"schemaVersion": 1. - Field names and the spellings of
protocol,addressFamily,process.statusandsignature.statusare a contract. Incompatible changes bumpschemaVersion; check it before reading anything else. - The document is always written, even when nothing matched: you then get
"endpointCount": 0, an emptyendpointsarray and exit code1. - Properties are camelCase, the output is indented, and absent values are written as
nullrather than omitted, so every field below is always present. - Endpoints are sorted by port, then protocol (TCP before UDP), then address family (IPv4 before IPv6), then address, then process id.
Document shape
{
"schemaVersion": 1,
"generatedAt": "2026-10-08T12:00:00+00:00",
"endpointCount": 1,
"endpoints": [
{
"protocol": "tcp",
"addressFamily": "ipv4",
"localAddress": "0.0.0.0",
"localPort": 135,
"processId": 1116,
"process": {
"status": "resolved",
"name": "svchost",
"executablePath": "C:\\Windows\\System32\\svchost.exe",
"services": [
{ "name": "RpcEptMapper", "displayName": "RPC Endpoint Mapper" },
{ "name": "RpcSs", "displayName": "Remote Procedure Call (RPC)" }
],
"signature": {
"status": "signed",
"signer": "Microsoft Windows",
"organization": "Microsoft Corporation",
"issuer": "Microsoft Windows Production PCA 2011",
"detail": null
},
"detail": null
}
}
]
}
Fields
Top level
| Field | Type | Meaning |
|---|---|---|
schemaVersion |
integer | Always 1 for this contract. |
generatedAt |
string | When the run happened: an ISO 8601 timestamp in UTC, with offset. |
endpointCount |
integer | Number of entries in endpoints. |
endpoints |
array | One entry per endpoint, exactly as Windows reports them. Two UDP sockets sharing an address and port through SO_REUSEADDR appear twice. |
Endpoint
| Field | Type | Meaning |
|---|---|---|
protocol |
string | tcp (listening socket) or udp (bound socket). |
addressFamily |
string | ipv4 or ipv6. |
localAddress |
string | The bound local address, without brackets for IPv6. |
localPort |
integer | The local port, 0–65535. |
processId |
integer | Owning process id as reported by Windows. |
process |
object | What PortWho knows about the owning process. |
Process
| Field | Type | Meaning |
|---|---|---|
status |
string | How far the lookup got; see the enumeration below. |
name |
string or null | Process name without extension (as Get-Process shows it). May be known even when status is accessDenied. |
executablePath |
string or null | Full path of the executable image, when the process could be opened. |
services |
array | Windows services hosted by the process; empty when none or unknown. |
signature |
object | Authenticode verdict for the executable image. |
detail |
string or null | Human-readable explanation of a partial or failed lookup. |
Service
| Field | Type | Meaning |
|---|---|---|
name |
string | Service key name, as used by sc and Get-Service -Name. |
displayName |
string | Human-readable service name. |
Signature
| Field | Type | Meaning |
|---|---|---|
status |
string | Verification outcome; see the enumeration below. |
signer |
string or null | Common name of the signing certificate’s subject, as Windows shows it under “Name of signer”. |
organization |
string or null | Organization (O=) of the signing certificate’s subject; often more recognizable than signer. |
issuer |
string or null | Common name of the certificate issuer. |
detail |
string or null | Extra explanation, typically the Windows error text and code for a failed verification or an unreadable file. |
For a catalog-signed file, signer, organization and issuer describe the catalog’s signer.
Enumerations
process.status:
| Value | Meaning |
|---|---|
resolved |
The process was opened and its image path read. |
accessDenied |
Windows refused to open the process; usually a protected or higher-privileged process. name may still be known. |
notFound |
The process no longer exists; it exited between the endpoint snapshot and the lookup. |
system |
A kernel-owned pseudo process (PID 0 or 4) with no user-mode executable. services is empty and the signature is notChecked. |
failed |
The lookup failed for another reason; see detail. |
signature.status:
| Value | Meaning |
|---|---|
notChecked |
No check was attempted: --no-signature was given, or no executable path was available. |
signed |
The file carries a valid embedded Authenticode signature. |
catalogSigned |
The file is unsigned itself but is listed in a valid Windows security catalog. |
unsigned |
No embedded signature and no catalog entry were found. |
invalid |
A signature or catalog entry exists but failed verification (bad digest, untrusted chain, expired, and so on). |
unavailable |
The file could not be read, so no verdict is possible. |
All values are lowercase literals exactly as spelled here.
Stability promise
Within schema version 1, field names and enumeration spellings do not change. Any incompatible change ships with a new schemaVersion, so treat an unknown version as “stop and update the consumer” rather than guessing.
PowerShell example
ConvertFrom-Json turns the document into objects you can filter and project:
$report = portwho --json | ConvertFrom-Json
if ($report.schemaVersion -ne 1) {
throw "Unexpected PortWho schema version $($report.schemaVersion)."
}
# Listeners that are not signed by a verified publisher
$report.endpoints |
Where-Object { $_.process.signature.status -in 'unsigned', 'invalid', 'unavailable' } |
Select-Object protocol, localAddress, localPort, processId,
@{ Name = 'process'; Expression = { $_.process.name } },
@{ Name = 'services'; Expression = { $_.process.services.name -join ', ' } } |
Format-Table
Remember that the exit code is 1 when nothing matched even though a valid document was written; check $LASTEXITCODE if your script treats “no owner” differently from “owner found”.