The greet CLI probes remote servers across layered protocol stacks and reports per-layer timing and data. It supports human-readable and JSON output, and auto-discovers protocol-specific flags from registered layers.
go install github.com/crystade/greet/cmd/greet@latestOr build from source:
cd cmd/greet
go build -o greet .# Check if a TLS server is reachable and inspect its certificate
greet tls example.com
# Check an SSH server on a non-standard port
greet ssh 10.0.0.5:2222
# Check if a Minecraft server is online
greet minecraft mc.hypixel.netgreet <protocol> <host>[:<port>] [flags]
The protocol name must be one of the registered protocols (see Supported protocols in Core.md). The target is host or host:port. When no port is given, the protocol's default is used.
Flags must appear after the protocol and target:
# Correct
greet minecraft mc.example.com --protocol-version 766 --timeout 3s
# Also correct — flags can be interspersed after the target
greet tls example.com:8443 --output-format json --timeout 10s
# Wrong — flags before the target will be misinterpreted
greet tls --timeout 3s example.com ❌Use bracket notation for IPv6 addresses:
greet tcp [::1]:8080
greet tls [2606:4700:4700::1111]:443| Flag | Type | Default | Description |
|---|---|---|---|
--timeout |
duration | 5s |
Total operation timeout, including DNS. Accepts 3s, 1m, 500ms, etc. |
--output-format |
string | "default" |
Output format: "default" (human-readable) or "json". |
Protocol layers can expose CLI flags by registering flagHandler entries in the CLI's init() registry. Each protocol has a dedicated flags_*.go file in cmd/greet/ that registers and parses its flags into GreetOption values.
| Flag | Type | Default | Description |
|---|---|---|---|
--method |
string | "GET" |
HTTP method (e.g. GET, POST, HEAD). |
--path |
string | "/" |
Request path and optional query (e.g. /users?sort=name). |
--max-body |
int | 1 MiB | Maximum response body bytes to buffer. |
--max-redirects |
int | 0 |
Maximum redirects to follow (0 = none). |
--header |
string | Request header in Name:Value form; may be repeated. |
greet http example.com --method POST --path /login --max-redirects 3 --header "Content-Type:application/json"| Flag | Type | Default | Description |
|---|---|---|---|
--protocol-version |
int | 775 |
Minecraft protocol version (e.g. 775 for 1.20.4, 766 for 1.20.2). |
greet minecraft mc.example.com --protocol-version 766| Flag | Type | Default | Description |
|---|---|---|---|
--sslmode |
string | "prefer" |
SSL mode: "prefer" (try SSL) or "disable" (skip SSL probe). |
greet postgresql db.example.com --sslmode disable| Flag | Type | Default | Description |
|---|---|---|---|
--path |
string | "/" |
Request path for MCP streamable HTTP (e.g. /mcp). |
--bearer-token |
string | "" |
Bearer token for MCP Authorization header. |
--list-resources |
bool | false |
List server resources after the handshake. |
--list-tools |
bool | false |
List server tools after the handshake. |
--list-prompts |
bool | false |
List server prompts after the handshake. |
--list-resource-templates |
bool | false |
List resource templates after the handshake. |
greet mcp mcp.example.com --list-tools --list-resources
greet mcp mcp.example.com --path /mcp
greet mcp mcp.example.com --bearer-token "sk-abc123" --list-tools| Flag | Type | Default | Description |
|---|---|---|---|
--server-name |
string | (target host) | TLS SNI hostname override. |
--client-cert |
string | Path to PEM client certificate for mTLS (key may be bundled). | |
--client-key |
string | Path to PEM client private key (default: bundled in --client-cert). |
|
--ca-cert |
string | Path to PEM CA certificate for trust verification. | |
--insecure |
bool | true |
Skip certificate verification. |
greet tls internal.example.com --server-name public.example.com --insecure
greet tls mtls.example.com --client-cert cert.pem --client-key key.pem --insecure=falsegreet listOutput:
Available protocols:
http :80 HTTP request/response probe
mcp :443 MCP streamable HTTP initialize handshake + capability probe
minecraft :25565 Minecraft Java Edition handshake + status request
postgresql :5432 PostgreSQL SSLRequest probe
ssh :22 SSH version banner exchange
tcp :80 Raw TCP connectivity probe
tls :443 TLS certificate check
udp :53 UDP connectivity probe
greet --help
greet -hShows protocol-level metrics followed by each layer's timing and protocol-specific data.
TLS:
$ greet tls example.comProtocol: tls
Transport: tcp
TTDR: 6.5219ms
RTT: 18.6273ms
Success: true
[tcp]
TTFB: 18.6273ms
TTLB: 18.6273ms
Success: true
[tls]
TTFB: 156.8465ms
TTLB: 156.8465ms
Success: true
Subject: CN=www.example.org
Issuer: CN=DigiCert Global G2 TLS RSA SHA256 2020 CA1
Serial: 0abc...
Not Before: 2025-01-15T00:00:00Z
Not After: 2026-02-15T23:59:59Z
Version: 3
DNS Names: [www.example.org example.com]
Signature Algorithm: SHA256-RSA
Public Key Algorithm: RSA
Fingerprint (SHA-256): a1b2c3...
Chain Status: ok
Certificate Chain (3 certificates, leaf → root):
[0] (leaf)
Subject: CN=www.example.org
Issuer: CN=DigiCert Global G2 TLS RSA SHA256 2020 CA1
Serial: 0abc...
Not Before: 2025-01-15T00:00:00Z
Not After: 2026-02-15T23:59:59Z
Fingerprint: a1b2c3...
Status: ok
[1] (intermediate)
Subject: CN=DigiCert Global G2 TLS RSA SHA256 2020 CA1
...
[2] (root)
Subject: CN=DigiCert Global Root G2
...
HTTP:
$ greet http example.com --max-redirects 1Protocol: http
Transport: tcp
TTDR: 5.1ms
RTT: 14.2ms
Success: true
[tcp]
TTFB: 14.2ms
TTLB: 14.2ms
Success: true
[http]
TTFB: 45.6ms
TTLB: 46.1ms
Success: true
Protocol Version: HTTP/1.1
Status: 200 OK
Status Code: 200
SSH:
$ greet ssh github.comProtocol: ssh
Transport: tcp
TTDR: 5.2ms
RTT: 12.8ms
Success: true
[tcp]
TTFB: 12.8ms
TTLB: 12.8ms
Success: true
[ssh]
TTFB: 45.3ms
TTLB: 45.3ms
Success: true
Version String: SSH-2.0-babeld-77e6c0e7
Minecraft:
$ greet minecraft mc.hypixel.netProtocol: minecraft
Transport: tcp
TTDR: 3.1ms
RTT: 28.5ms
Success: true
[tcp]
TTFB: 28.5ms
TTLB: 28.5ms
Success: true
[minecraft]
TTFB: 112.7ms
TTLB: 113.1ms
Success: true
Version: 1.8.9
MOTD: §aHypixel Network §c[1.8-1.21]
Players Online: 46832
Players Max: 200000
PostgreSQL:
$ greet postgresql db.example.comProtocol: postgresql
Transport: tcp
TTDR: 4.2ms
RTT: 15.1ms
Success: true
[tcp]
TTFB: 15.1ms
TTLB: 15.1ms
Success: true
[postgresql]
TTFB: 18.9ms
TTLB: 18.9ms
Success: true
SSL Supported: true
MCP:
$ greet mcp mcp.example.com --list-toolsProtocol: mcp
Transport: tcp
TTDR: 10.2ms
RTT: 28.7ms
Success: true
[tcp]
TTFB: 28.7ms
TTLB: 28.7ms
Success: true
[tls]
TTFB: 55.1ms
TTLB: 62.3ms
Success: true
Subject: CN=mcp.example.com
Issuer: CN=DigiCert Global G2 TLS RSA SHA256 2020 CA1
Serial: 1f2a...
Not Before: 2025-06-01T00:00:00Z
Not After: 2026-07-01T23:59:59Z
Version: 3
DNS Names: [mcp.example.com]
Signature Algorithm: SHA256-RSA
Public Key Algorithm: RSA
Fingerprint (SHA-256): d4e5f6...
Chain Status: ok
[mcp]
TTFB: 75.9ms
TTLB: 145.2ms
Success: true
Protocol Version: 2026-07-28
Server: example-mcp-server 2.1.0
Instructions: Use this server to query the example API.
Capabilities: tools
Tools (1):
- search
Search the example dataset.
Use --output-format json for machine-readable output. The schema is fully described in JSON API.md.
greet tls example.com --output-format json{
"protocol": "tls",
"transport": "tcp",
"ttdrMs": 6.5219,
"rttMs": 18.6273,
"success": true,
"layers": [
{
"name": "tcp",
"ttfbMs": 18.6273,
"ttlbMs": 18.6273,
"success": true
},
{
"name": "tls",
"ttfbMs": 156.8465,
"ttlbMs": 156.8465,
"success": true,
"data": {
"certChain": [
{
"subject": "CN=www.example.org",
"issuer": "CN=DigiCert Global G2 TLS RSA SHA256 2020 CA1",
"serial": "0abc...",
"notBefore": "2025-01-15T00:00:00Z",
"notAfter": "2026-02-15T23:59:59Z",
"version": 3,
"dnsNames": ["www.example.org", "example.com"],
"isCa": false,
"signatureAlgo": "SHA256-RSA",
"publicKeyAlgo": "RSA",
"sha256Fingerprint": "a1b2c3...",
"status": ["ok"]
}
],
"status": ["ok"]
}
}
]
}When a probe fails, the CLI prints the error to stderr and exits with code 1. If partial results are available (e.g. TCP succeeded but TLS failed), they are printed before the error.
$ greet tls 10.0.0.99:443[tcp]
TTFB: 5000ms
TTLB: 5000ms
Success: false
Error: [tcp] connection_timeout: dial tcp 10.0.0.99:443: i/o timeout
Code: connection_timeout
Protocol: tcp
| Code | Meaning |
|---|---|
0 |
All layers succeeded. |
1 |
Probe failed (connection error, handshake error, unknown protocol, invalid arguments). |
The JSON output is designed for pipelines:
# Check if TLS is valid for a domain
greet tls example.com --output-format json | jq '.success'
# Extract the TLS certificate subject
greet tls example.com --output-format json | jq '.layers[] | select(.name=="tls") | .data.certChain[0].subject'
# Get Minecraft player count
greet minecraft mc.hypixel.net --output-format json | jq '.layers[] | select(.name=="minecraft") | .data.players'
# List MCP server tools
greet mcp mcp.example.com --output-format json --list-tools | jq '.layers[] | select(.name=="mcp") | .data.tools[] | .name'
# Extract MCP server version
greet mcp mcp.example.com --output-format json | jq '.layers[] | select(.name=="mcp") | .data.serverInfo.version'
# Wait for a server to come online
while ! greet tcp my-server:8080 --timeout 2s; do sleep 5; done
# Check multiple servers
for host in server1.example.com server2.example.com; do
greet tls "$host" --output-format json | jq '{host: "'"$host"'", success: .success, rttMs: .rttMs}'
done--timeout sets the deadline for the entire operation including DNS resolution. The default is 5 seconds.
# Aggressive 1-second timeout for local network checks
greet tcp 192.168.1.50:3000 --timeout 1s
# Patient 30-second timeout for slow connections
greet tls overseas.example.com --timeout 30sThe timeout covers DNS + TCP dial + all layer handshakes combined. If the total exceeds the limit, the operation fails with a deadline_exceeded or connection_timeout error.
To add CLI flags for a new protocol, create a flags_<name>.go file in cmd/greet/. The file registers a flagHandler via init() that defines flag registration and parsing logic:
// cmd/greet/flags_myproto.go
package main
import (
"flag"
"github.com/crystade/greet"
"github.com/crystade/greet/protocols/myproto"
)
func init() {
registerFlagHandler(myproto.ProtocolName, flagHandler{
register: registerMyProtoFlags,
parse: parseMyProtoFlags,
})
}
func registerMyProtoFlags(fs *flag.FlagSet) {
fs.Int("my-flag", 1234, "My custom flag")
}
func parseMyProtoFlags(fs *flag.FlagSet) ([]greet.GreetOption, error) {
val := 1234
if f := fs.Lookup("my-flag"); f != nil {
if getter, ok := f.Value.(flag.Getter); ok {
if v, ok := getter.Get().(int); ok {
val = v
}
}
}
return []greet.GreetOption{
greet.WithLayerConfig(myproto.ProtocolName, &myproto.MyProtoConfig{MyField: val}),
}, nil
}The CLI automatically discovers registered handlers when it resolves a protocol stack. No changes to the core library or protocol package are needed.