Tài liệu PeerovaPeerova documentation
Peerova là tunnel P2P trực tiếp + SDK cho mọi giao thức TCP/UDP. Client nối thẳng tới dịch vụ của bạn sau NAT: không relay, không mở port công khai, mã hoá đầu-cuối.Peerova is a direct P2P tunnel + SDK for any TCP/UDP protocol. Clients reach your service behind NAT directly: no relay, no public open ports, end-to-end encrypted.
Bắt đầu nhanh (5 phút)Quickstart (5 minutes)
- Tải
peerovacho máy chạy dịch vụ và máy client (Tải về).Downloadpeerovaon the machine running your service and on the client (Download). - Chẩn đoán mạng cả hai máy:Diagnose the network on both machines:
peerova doctor - Tạo khoá định danh server (một lần, giữ bí mật):Create the server identity key (once, keep it secret):
peerova keygen # writes server.key - Chạy server với license và các dịch vụ muốn expose:Run the server with your license and the services to expose:
peerova server --port 47000 --license PVA-XXXXX-XXXXX-XXXXX-XXXXX --expose tcp:443 # TICKET pvt_.... <- give it to authorized clients - Phía client:On the client:
peerova client pvt_.... --forward tcp:8443=443 # now open https://127.0.0.1:8443
--license để chạy dev mode miễn phí: 1 server, 3 kết nối đồng thời.No license? Omit --license for free dev mode: 1 server, 3 concurrent connections.Web app / API (dev & staging)Web app / API (dev & staging)
Chia sẻ localhost/staging mà không qua tunnel relay: HTTPS đi đầu-cuối, không subdomain công khai.Share localhost/staging without a relay tunnel: HTTPS stays end-to-end, no public subdomain.
peerova server --license $PEEROVA_LICENSE --private --expose tcp:3000 --expose tcp:443
peerova client pvt_... --forward tcp:3000 --forward tcp:8443=443
curl http://127.0.0.1:3000/api/health
SSH / RDP cho văn phòng (không cần thiết bị VPN)SSH / RDP for offices (no VPN appliance)
# backend/admin machine: access signing key (keeps access.key, prints pvak_...)
peerova keygen --access --key access.key
# office server: only accept tokens signed by that key
peerova server --license $PEEROVA_LICENSE --access-key pvak_... \
--expose tcp:22 --expose tcp:3389 --expose udp:3389 --audit-log /var/log/peerova/audit.jsonl
# issue an 8-hour SSH-only token for alice
peerova token issue --access-key access.key --ports tcp:22 --ttl 8h --sub alice
# alice
peerova client pvt_... --access-token pva1.... --forward tcp:2222=22
ssh -p 2222 alice@127.0.0.1
Database cho team từ xaDatabases for remote teams
peerova server --license $PEEROVA_LICENSE --access-key pvak_... --expose tcp:5432 --expose tcp:6379
peerova client pvt_... --access-token pva1.... --forward tcp:5432 --forward tcp:6379
psql -h 127.0.0.1 -U app mydb
IoT / thiết bị sau CGNAT (router 4G, camera)IoT / devices behind CGNAT (4G routers, cameras)
# on the device (ARM build): RTSP + web UI
peerova server --license $PEEROVA_LICENSE --expose tcp:554 --expose tcp:80
# operator
peerova client pvt_... --forward tcp:8554=554 --forward tcp:8080=80
ffplay rtsp://127.0.0.1:8554/stream
Sau CGNAT: Peerova ưu tiên IPv6 (đa số mạng 4G VN có), sau đó hole punch qua supernode.Behind CGNAT Peerova prefers IPv6 (most VN mobile networks have it), then hole punching via supernodes.
Game server
peerova server --license $PEEROVA_LICENSE --expose udp:7777 --expose tcp:25565
peerova client pvt_... --forward udp:7777 --forward tcp:25565
Hoặc nhúng SDK để không cần chạy CLI riêng — xem SDK.Or embed the SDK instead of running the CLI — see SDK.
Tham chiếu CLICLI reference
Chạy peerova <lệnh> --help để xem đầy đủ cho phiên bản bạn đang dùng.Run peerova <command> --help for the exact options of your version.
| LệnhCommand | CờFlag | Mô tảDescription |
|---|---|---|
| peerova doctor | Chẩn đoán mạng: IPv6, UPnP, NAT-PMP, CGNAT, double NAT.Network diagnosis: IPv6, UPnP, NAT-PMP, CGNAT, double NAT. | |
| peerova keygen | [--key server.key] | Tạo danh tính server (file pvk_…); ticket cố định theo key.Create the server identity (file pvk_…); the ticket stays stable for that key. |
| peerova keygen --access | [--key access.key] | Tạo khoá cấp quyền doanh nghiệp (giữ ở backend/SSO); in public key pvak_… cho --access-key.Create the enterprise access-signing key (keep it in your backend/SSO); prints the public key pvak_… for --access-key. |
| peerova token issue | --access-key access.key --sub alice --ports tcp:443,udp:10000-10100 --ttl 8h | Phát token truy cập pva1.…: ký, có hạn, giới hạn port, gắn danh tính.Mint an access token pva1.…: signed, expiring, port-scoped, bound to an identity. |
| peerova server | [--config peerova.json] [--port 47000] [--key server.key] | Chạy server. Mọi cờ đều đặt được trong file cấu hình.Run a server. Every flag can live in the config file. |
| --expose PROTO:PORT[-PORT][=HOST:PORT] | Dịch vụ cho phép truy cập; lặp lại; có dải. PROTO: tcp http https ws wss ssh rdp grpc (TCP) · udp quic game (UDP). UDP tới 64 KB.Services clients may reach; repeatable; ranges. PROTO: tcp http https ws wss ssh rdp grpc (TCP) · udp quic game (UDP). UDP up to 64 KB. | |
| --license PVA-… | License key (hoặc PEEROVA_LICENSE); bỏ trống = dev mode (1 server, 3 kết nối).License key (or PEEROVA_LICENSE); omit for dev mode (1 server, 3 connections). | |
| --private · --no-map · --no-tcp · --max-ccu N · --ticket-file FILE | Ticket không chứa IP · tắt UPnP/NAT-PMP · tắt TCP fallback · giới hạn kết nối · ghi ticket ra file.Ticket without IPs · disable UPnP/NAT-PMP · disable TCP fallback · cap connections · write the ticket to a file. | |
| --token SECRET | --access-key PUB [--revoked revoked.txt] | Mã tham gia dùng chung, hoặc chỉ nhận token ký bởi pvak_… (kèm danh sách thu hồi).Shared join secret, or accept only tokens signed by pvak_… (with a revocation list). | |
| --audit-log audit.jsonl | Mỗi kết nối một dòng JSON cho SIEM.One JSON line per connection for your SIEM. | |
| --supernode HOST:PORT · --supernode-mode · --no-auto-supernode | Supernode thủ công · chạy như supernode · không tự lấy danh sách supernode.Pin supernodes · act as a supernode · do not fetch the supernode list. | |
| --rdv | --rendezvous URL | Website làm điểm hẹn → không cần VPS/supernode/relay.Use the website as rendezvous → no VPS/supernode/relay. | |
| --app PRJ --share "Tên dịch vụ" [--public] [--max N] [--mode M] [--region VN] [--tag T] | Công bố dịch vụ chia sẻ, nhận mã kết nối 6 ký tự (bí danh: --lobby) (xem Mã kết nối).Announce a service/room and get a 6-char connect code (see Connect codes). | |
| --metrics 127.0.0.1:9184 | Endpoint Prometheus (xem Metrics).Prometheus endpoint (see Metrics). | |
| peerova client | <TICKET|@file|CODE> --forward PROTO:LOCAL[-LOCAL][=REMOTE] | Mở 127.0.0.1:LOCAL và tunnel tới port REMOTE đã expose; nhận ticket, file ticket hoặc mã 6 ký tự.Listen on 127.0.0.1:LOCAL and tunnel to the exposed REMOTE port; accepts a ticket, a ticket file or a 6-char code. |
| [--bind IP] [--access-token TOKEN | --token SECRET] [--reconnect] [--tcp] [--site URL] | Địa chỉ bind · token truy cập · tự kết nối lại · ép TCP · website khác mặc định.Bind address · access token · auto-reconnect · force TCP · non-default website. | |
| peerova ping | <TICKET|@file> [--count 50] [--interval-ms 100] [--bench-mb 8] [--tcp] | Đo RTT, jitter, mất gói và thông lượng.Measure RTT, jitter, loss and throughput. |
| peerova stdio | server ['<json>'] | client <TICKET> ['<json>'] | SDK cho mọi ngôn ngữ qua JSON lines stdin/stdout (sidecar).SDK for any language via JSON lines on stdin/stdout (sidecar). |
| peerova ticket merge | <T1> <T2>… | Gộp ticket nhiều máy cùng server.key → client tự failover.Merge tickets of several machines sharing server.key → clients fail over automatically. |
| peerova lease-info | <LEASE> [--authority-pubkey B64] | Giải mã và kiểm tra một lease pvl1.….Decode and verify a pvl1.… lease. |
File cấu hìnhConfig file
peerova server --config peerova.json — mọi cờ đều có khoá tương ứng. Phù hợp khi chạy như service.peerova server --config peerova.json — every flag has a matching key. Ideal when running as a service.
{
"port": 47000,
"key_file": "/etc/peerova/server.key",
"license": "PVA-XXXXX-XXXXX-XXXXX-XXXXX",
"private": true,
"expose": ["tcp:443", "tcp:22", "udp:10000-10100"],
"access_key": "pvak_...",
"audit_log": "/var/log/peerova/audit.jsonl",
"tcp_fallback": true,
"supernodes": []
}
Giao thứcProtocols
Peerova tunnel TCP stream và UDP datagram, nên mọi giao thức phía trên chạy nguyên vẹn. HTTPS/TLS không bao giờ bị giải mã.Peerova tunnels TCP streams and UDP datagrams, so every protocol on top runs unchanged. HTTPS/TLS is never decrypted.
| Giao thứcProtocol | Transport | Hỗ trợSupported | Ghi chúNotes |
|---|---|---|---|
| TCP (any app) | TCP | ✓ | Mọi ứng dụng TCP: --expose tcp:PORTAny TCP app: --expose tcp:PORT |
| UDP (any app) | UDP | ✓ | Datagram tới 64 KB nhờ phân mảnh trong tunnelDatagrams up to 64 KB via in-tunnel fragmentation |
| HTTP/1.1 · HTTP/2 | TCP | ✓ | Không cần reverse proxy, Host header giữ nguyênNo reverse proxy, Host header untouched |
| HTTPS / TLS | TCP | ✓ | Đầu-cuối: Peerova không giải mã, chứng chỉ của bạn giữ nguyênEnd-to-end: never decrypted, your certificate stays |
| WebSocket / WSS | TCP | ✓ | Kết nối dài, upgrade bình thườngLong-lived, normal upgrade |
| gRPC | TCP (HTTP/2) | ✓ | Streaming hai chiềuBidirectional streaming |
| QUIC / HTTP/3 | UDP | ✓ | Forward UDP 443Forward UDP 443 |
| SSH · SFTP | TCP | ✓ | --expose tcp:22--expose tcp:22 |
| RDP · VNC | TCP (+UDP) | ✓ | RDP: expose cả tcp:3389 và udp:3389 để mượt hơnRDP: expose both tcp:3389 and udp:3389 for smoother sessions |
| MySQL · Postgres · Redis · MongoDB | TCP | ✓ | Giữ TLS của DB nếu có; không cần mở port DB ra InternetKeep DB TLS if used; never expose the DB port publicly |
| MQTT | TCP | ✓ | 1883 / 8883 (TLS)1883 / 8883 (TLS) |
| DNS | UDP + TCP | ✓ | Expose cả udp:53 và tcp:53Expose both udp:53 and tcp:53 |
| VoIP · SIP · RTP | UDP | ✓ | Dải port RTP: --expose udp:10000-10100RTP port range: --expose udp:10000-10100 |
| FTP (passive) | TCP | ◐ | Expose tcp:21 + dải passive (vd. tcp:30000-30100) và cấu hình server FTP dùng đúng dải đóExpose tcp:21 + the passive range (e.g. tcp:30000-30100) and pin the FTP server to it |
| Game UDP | UDP | ✓ | Gói game, voice chat; CCU giới hạn theo góiGame packets, voice; CCU per plan |
| ICMP · raw IP (ping, L3 VPN) | — | ✗ | Chưa hỗ trợ; chế độ VPN L3 nằm trong roadmapNot yet; an L3 VPN mode is on the roadmap |
Ticket
Ticket pvt_… là chuỗi bạn trao cho client được phép. Nó chứa khoá công khai Ed25519 của server và gợi ý tìm đường (supernode), không chứa IP thật của server. Client dùng ticket để xác thực đúng server (chống giả mạo) trước khi trao đổi khoá Noise.A ticket pvt_… is what you hand to authorized clients. It carries the server's Ed25519 public key and rendezvous hints (supernodes) — not the server's real IP. Clients use it to authenticate the right server (no impersonation) before the Noise handshake.
Không cần VPS: website làm điểm hẹnNo VPS needed: the website is the rendezvous
Bạn không cần thuê VPS, supernode hay relay. Server Peerova hỏi website mỗi ~2 giây (POST /api/v1/rdv/poll) để công bố địa chỉ; client trình ticket rồi hỏi website (POST /api/v1/rdv/connect) để lấy địa chỉ của server. Hai bên cùng nhận địa chỉ của nhau và đục NAT trực tiếp.You do not need a VPS, a supernode or a relay. A Peerova server asks the website every ~2 s (POST /api/v1/rdv/poll) to announce its addresses; a client holding the ticket asks the website (POST /api/v1/rdv/connect) for the server's addresses. Both sides learn each other's addresses and punch through NAT directly.
- Dữ liệu không bao giờ đi qua website. Website chỉ trao đổi địa chỉ (IP công khai nhìn thấy, port, địa chỉ LAN/IPv6) lúc bắt đầu kết nối; sau đó mọi gói đi thẳng peer ↔ peer, mã hoá đầu-cuối.Data never passes through the website. It only exchanges addresses (observed public IP, port, LAN/IPv6 addresses) when a connection starts; afterwards every packet goes peer ↔ peer, end-to-end encrypted.
- Thông tin hẹn tự hết hạn sau 30 giây; mỗi yêu cầu của client chỉ được giao cho server một lần. Ai có ticket thì tra được địa chỉ server — cùng mức tin cậy với chính ticket.Rendezvous entries expire after 30 seconds; each client request is delivered to the server once. Anyone holding the ticket can look up the server address — the same trust level as the ticket itself.
- Giới hạn: nếu cả hai bên đều sau NAT đối xứng (symmetric) cấp port ngẫu nhiên và không có IPv6, vẫn không kết nối được — điểm hẹn không thay được relay. Bật IPv6 hoặc UPnP ở phía server để chắc chắn.Limit: if both sides are behind random-port symmetric NATs without IPv6, they still cannot connect — a rendezvous is not a relay. Enable IPv6 or UPnP on the server side to be safe.
Supernode
Mọi peerova server reachable cũng là supernode: làm STUN (cho biết địa chỉ public) và giới thiệu hai peer cho nhau để hole punch. Supernode không chuyển dữ liệu của bạn. Có thể chỉ định thủ công bằng --supernode IP:PORT hoặc "supernodes":[] trong config SDK.Every reachable peerova server is also a supernode: it acts as STUN (tells peers their public address) and introduces two peers for hole punching. Supernodes never carry your data. Set them manually with --supernode IP:PORT or "supernodes":[] in the SDK config.
Dark server
Server không mở cổng nào trên router và bỏ qua im lặng mọi gói chưa xác thực: không trả lời, không lộ phiên bản, không lộ dịch vụ. Chỉ client có ticket hợp lệ và được phép mới nhận được địa chỉ để kết nối.The server opens no router ports and silently drops every unauthenticated packet: no reply, no version banner, no service fingerprint. Only authorized clients holding a valid ticket receive the address to connect to.
Chống crack: thực thi ở phía mạngAnti-crack: enforced by the network
Lease có thể mang thêm spk — khoá công khai tĩnh của server (contract v2). Core kiểm tra spk trùng khoá của chính nó; supernode chỉ nhận đăng ký kèm lease do authority ký (chữ ký + hạn + spk). Vì vậy bản build bị crack/không có license không dùng được mạng supernode, dù sửa được binary.Leases may carry spk — the server's static public key (contract v2). The core checks spk equals its own key; supernodes only accept registrations that carry an authority-signed lease (signature + expiry + spk). Cracked or unlicensed builds therefore cannot use the supernode network, even if the binary is patched.
- Server có thể tự đăng ký làm supernode khi xin lease (
"supernode":"ip:port"). Danh sách công khai:GET /api/v1/supernodes— tối đa 32 địa chỉ hoạt động trong 30 phút từ license trả phí còn hạn.Servers can offer themselves as supernodes when requesting a lease ("supernode":"ip:port"). Public list:GET /api/v1/supernodes— up to 32 addresses active in the last 30 minutes from valid paid licenses. - Supernode không bao giờ chuyển dữ liệu người dùng — chỉ phản chiếu địa chỉ và giới thiệu peer.Supernodes never carry user data — they only reflect addresses and introduce peers.
- Người dùng có license không bao giờ bị bóp băng thông hay giới hạn tốc độ. Việc thực thi nằm ở phía mạng, không ở trải nghiệm của bạn.Licensed users are never throttled. Enforcement happens on the network side, never in your users' experience.
Phương thức kết nốiConnection methods
| # | Phương thứcMethod | Khi nào thành côngWhen it works |
|---|---|---|
| 1 | ipv6 | Cả hai có IPv6 global (đa số nhà mạng VN và 4G).Both sides have global IPv6 (most VN ISPs and 4G). |
| 2 | mapped | Router server bật UPnP-IGD/NAT-PMP và IP WAN là public (không CGNAT).Server router has UPnP-IGD/NAT-PMP and a public WAN IP (no CGNAT). |
| 3 | punched | Hole punch UDP qua supernode giới thiệu.UDP hole punching introduced by a supernode. |
| 4 | lan | Cùng mạng LAN.Same LAN. |
| 5 | join code | Không gì hoạt động: client in mã pvj_, dán vào console server.Nothing else works: the client prints a pvj_ code, paste it into the server console. |
Lease & thời gian ân hạnLeases & grace period
Server đổi license key lấy một lease ký Ed25519 (pvl1.…) từ máy chủ license. Core kiểm tra lease offline: chữ ký hợp lệ, mid khớp machine id, chưa quá exp.The server trades its license key for an Ed25519-signed lease (pvl1.…) from the license authority. The core verifies it offline: valid signature, mid equals the machine id, not past exp.
- Lease hạn 7 ngày; core gia hạn mỗi 6 giờ.Leases last 7 days; the core renews every 6 hours.
- Mất mạng tới máy chủ license: tiếp tục chạy tới khi lease hết hạn (tối đa 7 ngày).License server unreachable: keep running until the lease expires (up to 7 days).
- Số server = số machine id khác nhau trong 8 ngày gần nhất.Server count = distinct machine ids seen in the last 8 days.
Bảo mật EnterpriseEnterprise security
- E2E Noise IKE2E Noise IK — X25519 + ChaCha20-Poly1305; ticket ghim public key server nên không giả mạo được.X25519 + ChaCha20-Poly1305; the ticket pins the server public key, so impersonation fails.
- Dark serverDark server — Không cổng nào trả lời máy quét; gói chưa xác thực bị bỏ im lặng.No port answers scanners; unauthenticated packets are silently dropped.
- Token truy cập ký, ngắn hạn, theo portSigned, short-lived, per-port access tokens —
peerova token issue --access-key access.key --ports tcp:443 --ttl 8h --sub alice. Phát token từ backend SSO/IdP của bạn.peerova token issue --access-key access.key --ports tcp:443 --ttl 8h --sub alice. Mint them from your own SSO/IdP backend. - Allow-list & thu hồi khoá clientClient key allow-list & revocation — Chỉ khoá client được duyệt mới vào; thu hồi tức thì.Only approved client keys get in; revoke instantly.
- Audit log JSONJSON audit log — Mỗi kết nối: ai, khi nào, dịch vụ nào, bao nhiêu byte — đẩy thẳng vào SIEM.Every connection: who, when, which service, bytes — straight into your SIEM.
- Chạy như serviceRuns as a service — systemd / Windows service, cấu hình bằng
peerova.json.systemd / Windows service with apeerova.jsonconfig file. - Tự host máy chủ licenseSelf-hosted license authority — Gói Enterprise: chạy authority trên hạ tầng của bạn (Node/VPS).Enterprise plan: run the authority on your own infrastructure (Node/VPS).
- Dữ liệu không qua nhà cung cấpData never transits vendor servers — Phù hợp yêu cầu tuân thủ và lưu trú dữ liệu.Fits compliance and data-residency requirements.
Token truy cập (theo người, theo port, có hạn)Access tokens (per user, per port, expiring)
Server chạy với --access-key chỉ chấp nhận token ký bởi khoá access.key tương ứng. Token mang sub (danh tính), danh sách port được phép và thời hạn. Tích hợp SSO/IdP: backend của bạn xác thực người dùng (OIDC/SAML…) rồi gọi peerova token issue (hoặc ký cùng định dạng) để cấp token ngắn hạn.A server started with --access-key only accepts tokens signed by the matching access.key. Tokens carry a sub (identity), allowed ports and an expiry. SSO/IdP integration: your backend authenticates the user (OIDC/SAML…) and calls peerova token issue (or signs the same format) to hand out short-lived tokens.
peerova token issue --access-key access.key --ports tcp:443,udp:10000-10100 --ttl 8h --sub alice@corp.vn
# -> pva1.eyJ... (give to the client: --access-token pva1....)
Thu hồi: token hết hạn tự động; để chặn ngay, xoay --access-key hoặc đưa khoá client vào danh sách thu hồi trong file cấu hình.Revocation: tokens expire on their own; to cut access immediately rotate the --access-key or add the client key to the revocation list in the config file.
Audit log
--audit-log audit.jsonl: mỗi dòng là một sự kiện JSON, đẩy được vào SIEM (Elastic, Splunk, Loki…). Ví dụ (trường có thể mở rộng):--audit-log audit.jsonl: one JSON event per line, ready for your SIEM (Elastic, Splunk, Loki…). Example (fields may be extended):
{"ts":"2026-11-01T09:12:44Z","event":"connect","sub":"alice@corp.vn","client_key":"k7Q…","service":"tcp:22","method":"ipv6","remote":"[2001:db8::5]:51123"}
{"ts":"2026-11-01T10:40:02Z","event":"disconnect","sub":"alice@corp.vn","service":"tcp:22","bytes_in":1834221,"bytes_out":99812,"duration_s":5238}
Chạy như serviceRun as a service
Linux (systemd) — /etc/systemd/system/peerova.service
[Unit]
Description=peerova server
After=network-online.target
Wants=network-online.target
[Service]
ExecStart=/usr/local/bin/peerova server --config /etc/peerova/peerova.json
Restart=always
User=peerova
NoNewPrivileges=true
ProtectSystem=strict
ReadWritePaths=/var/log/peerova
[Install]
WantedBy=multi-user.target
sudo systemctl enable --now Peerova
Windows
sc.exe create peerova binPath= "C:\peerova\peerova.exe server --config C:\peerova\peerova.json" start= auto
sc.exe start peerova
Lưu trú dữ liệu & tự host authorityData residency & self-hosted authority
Dữ liệu ứng dụng không bao giờ đi qua máy chủ của chúng tôi. Thứ duy nhất server gửi lên là yêu cầu lease (key, machine id) và telemetry tổng hợp. Gói Enterprise có thể tự host toàn bộ máy chủ license (Node ≥ 20 trên VPS của bạn) — khi đó không có gì rời hạ tầng của bạn.Application data never transits our servers. The only things a server sends us are lease requests (key, machine id) and aggregate telemetry. Enterprise can self-host the whole license authority (Node ≥ 20 on your VPS) — then nothing leaves your infrastructure.
Cài đặt licenseLicense setup
- Đăng ký — key dùng thử 14 ngày hiện một lần duy nhất; hãy lưu lại. Mất key thì bấm “Tạo key mới” (key cũ mất hiệu lực).Sign up — your 14-day trial key is shown once; store it. Lost it? Click “Regenerate key” (the old key stops working).
- CLI:
--license PVA-…. SDK: trường"license"trong config JSON. Nên đọc từ biến môi trường, đừng commit key vào git.CLI:--license PVA-…. SDK: the"license"field of the config JSON. Read it from an environment variable; never commit keys. - Đổi máy: vào dashboard → License → “Thu hồi” máy cũ để giải phóng slot.Changing hardware: dashboard → License → “Revoke” the old machine to free its slot.
Lỗi leaseLease errors
| error | Ý nghĩaMeaning |
|---|---|
invalid_key | Key sai hoặc đã được tạo lại.Wrong key, or it was regenerated. |
expired | License hết hạn — gia hạn trong dashboard.License expired — renew in the dashboard. |
revoked | License hoặc máy này đã bị thu hồi.The license or this machine was revoked. |
server_limit | Vượt số server của gói (máy khác nhau trong 8 ngày).More distinct machines (last 8 days) than the plan allows. |
Tự host máy chủ license (Enterprise)Self-hosting the authority (Enterprise)
Toàn bộ web + license chạy được trên VPS bằng Node (server/node-server.mjs). Khoá công khai lấy tại GET /api/v1/pubkey.The whole web + license service runs on a VPS with Node (server/node-server.mjs). The public key is at GET /api/v1/pubkey.
Mã kết nối & dịch vụ chia sẻConnect codes & shared services
Server có thể công bố một dịch vụ chia sẻ lên website và nhận mã kết nối 6 ký tự (vd. K7Q2XD). Người nhận chỉ cần mã thay cho ticket dài. Website chỉ lưu metadata công khai; dữ liệu vẫn đi thẳng P2P. Mã hết hạn 45 giây sau heartbeat cuối (server gửi mỗi ≤ 15 giây).A server can announce a service / room to the website and get a 6-character connect code (e.g. K7Q2XD). Recipients use the code instead of a long ticket. The website only stores public metadata; data still flows peer-to-peer. Codes expire 45 s after the last heartbeat (sent every ≤ 15 s).
| Cờ serverServer flag | Ý nghĩaMeaning |
|---|---|
| --app prj_… | App ID công khai (ID dự án trong dashboard).Public app ID (the project ID in the dashboard). |
| --share "Tên dịch vụ" | Tên dịch vụ chia sẻ (≤ 64 ký tự); bật mã kết nối. Bí danh cũ --lobby vẫn được chấp nhận.Shared service name (≤ 64 chars); enables connect codes. The old alias --lobby is still accepted. |
| --public | Hiện trong danh sách dịch vụ công khai /connect?app=… (không lộ ticket).List it in the public directory /connect?app=… (ticket never shown). |
| --max N | Số kết nối tối đa hiển thị (0 = không giới hạn hiển thị).Maximum connections shown (0 = unlimited). |
| --mode M · --region VN · --tag T | Phân loại để lọc: vd. web, api, ssh, db, game; khu vực; tối đa 8 tag.Categories for filtering: e.g. web, api, ssh, db, game; region; up to 8 tags. |
Ví dụ theo use caseExamples by use case
# Demo web app / API (HTTPS end-to-end) for a client
peerova server --app prj_xxx --share "Demo web" --expose https:443
# -> connect code K7Q2XD
peerova client K7Q2XD --forward tcp:8443=443 # open https://localhost:8443
# SSH / RDP for staff (combine with --access-key for per-person tokens)
peerova server --app prj_xxx --share "Văn phòng HCM" --mode ssh --expose ssh:22 --expose rdp:3389
peerova client Q4M8ZP --forward tcp:2222=22 --forward tcp:3389
# Database for a remote team
peerova server --app prj_xxx --share "Postgres staging" --mode db --expose tcp:5432
peerova client H2W9CK --forward tcp:5432
# Game server in the public directory
peerova server --app prj_xxx --share "Server game VN" --public --max 16 --mode game --region VN --expose udp:7777
peerova client K7Q2XD --forward udp:7777
SDK
Cấu hình server (C ABI JSON, mọi binding; khoá share, bí danh lobby):Server config (C ABI JSON, every binding; key share, alias lobby):
{"port":47000,"license":"PVA-...","app":"prj_xxx",
"share":{"name":"Demo API","public":true,"max":50,"mode":"api","region":"VN"}}
Lấy mã bằng peerova_room_code() (Python: srv.room_code); client có thể truyền mã thay cho ticket vào peerova_client_connect. Ngôn ngữ không có FFI dùng sidecar peerova stdio.Read the code with peerova_room_code() (Python: srv.room_code); clients may pass the code instead of a ticket to peerova_client_connect. Languages without FFI use the peerova stdio sidecar.
HTTP API
POST /api/v1/lobby/upsert·POST /api/v1/lobby/close— do server gọi— called by the serverGET /api/v1/lobby/resolve?code=K7Q2XD→ ticket (giới hạn 60 lần/phút/IP)(60/min per IP)GET /api/v1/lobby/list?app=prj_x&mode=®ion=&q=·GET /api/v1/lobby/stats?app=prj_x— cho trang danh sách / menu ứng dụng— for directories and in-app menus
Trang nhập mã kết nối & danh sách dịch vụ công khai →Connect-code page & public service directory →
Metrics (Prometheus)Metrics (Prometheus)
peerova server --license $PEEROVA_LICENSE --expose tcp:443 --metrics 127.0.0.1:9184
curl http://127.0.0.1:9184/metrics
# HELP peerova_ccu Connected peers.
# TYPE peerova_ccu gauge
peerova_ccu 12
peerova_max_ccu 200
# TYPE peerova_connects_total counter
peerova_connects_total{path="ipv6"} 31
peerova_connects_total{path="punched"} 18
# TYPE peerova_rejected_total counter
peerova_rejected_total 1
# TYPE peerova_dropped_unauthenticated_total counter
peerova_dropped_unauthenticated_total 4096
# TYPE peerova_failures_total counter
peerova_failures_total 2
peerova_supernode 0
Bind vào 127.0.0.1 (hoặc mạng nội bộ) rồi cho Prometheus/Grafana Agent scrape. Bộ đếm byte (peerova_bytes_*) xuất hiện ở các phiên bản có hỗ trợ — kiểm tra output của bản bạn dùng.Bind to 127.0.0.1 (or an internal network) and let Prometheus / Grafana Agent scrape it. Byte counters (peerova_bytes_*) appear in versions that support them — check your build's output.
SDK — C ABI
Mọi binding gọi cùng một C ABI (peerova.h). Có 4 cách tích hợp: native FFI (mặc định, trong process), JNI (Java/Kotlin/Android), sidecar peerova stdio (JSON lines, cho ngôn ngữ không có FFI), hoặc crate Rust trực tiếp.Every binding wraps the same C ABI (peerova.h). Four integration styles: native FFI (default, in-process), JNI (Java/Kotlin/Android), the peerova stdio sidecar (JSON lines, for languages without FFI), or the Rust crate directly.
#include "peerova.h" // libpeerova.so · peerova.dll · libpeerova.dylib (or the static lib)
enum { PEEROVA_EVENT_CONNECTED = 1, PEEROVA_EVENT_DISCONNECTED = 2,
PEEROVA_EVENT_MESSAGE = 3, PEEROVA_EVENT_CONNECT_FAILED = 4 };
typedef struct { int32_t type; uint32_t peer; uint8_t channel; uint8_t reliable;
const uint8_t* data; uint32_t len; } peerova_event;
peerova_node* peerova_server_start(const char* config_json); // blocks until the ticket is ready (≤ ~3 s)
peerova_node* peerova_client_connect(const char* ticket_or_code, const char* config_json);
int32_t peerova_ticket(peerova_node*, char* buf, int32_t buf_len); // returns size incl. NUL
int32_t peerova_room_code(peerova_node*, char* buf, int32_t buf_len); // 6-char code, 0 if none
int32_t peerova_send(peerova_node*, uint32_t peer, uint8_t channel,
const uint8_t* data, uint32_t len, int32_t reliable); // unreliable max 1180 B
int32_t peerova_poll(peerova_node*, peerova_event* out); // 1 = event, 0 = none
int32_t peerova_poll_wait(peerova_node*, peerova_event* out, uint32_t timeout_ms);
void peerova_disconnect(peerova_node*, uint32_t peer);
uint32_t peerova_peer_count(peerova_node*);
void peerova_free(peerova_node*);
const char* peerova_last_error(void);
const char* peerova_version(void);
Server config JSON: port, key_file, license, private, token, max_ccu, upnp, tcp_fallback, supernodes, rendezvous, app, share. Client config: token, timeout_ms, supernodes, force_tcp. Thư viện được tìm theo PEEROVA_LIB nếu đặt.Server config JSON: port, key_file, license, private, token, max_ccu, upnp, tcp_fallback, supernodes, rendezvous, app, share. Client config: token, timeout_ms, supernodes, force_tcp. Bindings honour PEEROVA_LIB to locate the library.
24 nền tảng24 platforms
TESTED = test round-trip đã chạy PASS trên máy build (Linux x86_64); compile-checked = biên dịch được nhưng chưa chạy trong engine/thiết bị thật; UNTESTED = chưa có toolchain để chạy.TESTED = round-trip test passed on the build machine (Linux x86_64); compile-checked = builds but not yet run in the real engine/device; UNTESTED = toolchain not available yet.
| Ngôn ngữ / engineLanguage / engine | Cơ chếMechanism | Trạng tháiStatus |
|---|---|---|
| C / C++ | header | chạy đượcruns |
| Python 3.8+ | ctypes | TESTEDTESTED |
| C# (.NET Standard 2.1 / .NET 6+) | P/Invoke | TESTEDTESTED |
| Unity 2021.3+ (com.peerova.sdk) | P/Invoke + PeerovaBehaviour | compile-checkedcompile-checked |
| Godot 4.2+ .NET (addons/peerova) | P/Invoke + PeerovaNode | compile-checkedcompile-checked |
| Unreal Engine 5 | plugin, UPeerovaSubsystem | core C++ TESTED, UE chưa chạycore C++ TESTED, UE not run |
| Java 8+ | JNI | TESTEDTESTED |
| Kotlin (JVM) | JNI | TESTEDTESTED |
| Android (NDK arm64/armv7/x86_64) | JNI | compile-checkedcompile-checked |
| Go 1.20+ (github.com/peerova/peerova-go) | cgo | TESTEDTESTED |
| Rust (crate peerova) | native | TESTEDTESTED |
| Node.js ≥ 18 | sidecar stdio | TESTEDTESTED |
| Electron (main / utilityProcess) | sidecar stdio | UNTESTEDUNTESTED |
| Deno ≥ 1.38 | FFI (Deno.dlopen) | UNTESTEDUNTESTED |
| Bun ≥ 1.0 | FFI (bun:ffi) / sidecar | UNTESTEDUNTESTED |
| Dart 3 | dart:ffi | TESTEDTESTED |
| Flutter (Android / iOS / desktop) | dart:ffi | UNTESTEDUNTESTED |
| Swift 5.7+ (iOS / macOS / Linux) | C module CPeerova | UNTESTEDUNTESTED |
| Ruby 3.0+ | Fiddle | UNTESTEDUNTESTED |
| PHP 8.1+ (ext/ffi) | FFI | TESTEDTESTED |
| PHP 8.1+ (no ext) | sidecar stdio | TESTEDTESTED |
| LuaJIT 2.1 (LÖVE, OpenResty…) | ffi.cdef | TESTEDTESTED |
| Lua 5.1–5.4 | sidecar stdio | TESTEDTESTED |
| Any language (shell, Erlang, Perl…) | sidecar stdio (JSON lines) | TESTEDTESTED |
C# / .NET
preview API SDK đang ở bản preview và có thể thay đổi trước bản 1.0. CLI đã ổn định.The SDK API is a preview and may change before 1.0. The CLI is stable.
using Peerova; // csharp/Peerova.cs (.NET Standard 2.1 / .NET 6+)
using var srv = Server.Start(new ServerConfig { Port = 47000, License = "PVA-XXXXX-XXXXX-XXXXX-XXXXX" });
Console.WriteLine("ticket: " + srv.Ticket);
while (true)
{
if (srv.Poll(out Event ev, 100))
{
if (ev.Type == EventType.Connected) Console.WriteLine($"peer {ev.Peer} connected");
if (ev.Type == EventType.Message) srv.Send(ev.Peer, ev.Data, channel: ev.Channel, reliable: true); // echo
}
}Unity
preview API SDK đang ở bản preview và có thể thay đổi trước bản 1.0. CLI đã ổn định.The SDK API is a preview and may change before 1.0. The CLI is stable.
Chép com.peerova.sdk vào Packages/ (hoặc Package Manager → Add package from disk), đặt native lib vào Plugins/.Copy com.peerova.sdk into Packages/ (or Package Manager → Add package from disk) and put the native libs into Plugins/.
using UnityEngine;
using Peerova.Unity; // UPM package com.peerova.sdk (C# binding of the C ABI)
public class NetHost : MonoBehaviour
{
void Start()
{
var net = gameObject.AddComponent<PeerovaBehaviour>();
net.OnHostStarted.AddListener(ticket => Debug.Log("ticket: " + ticket));
net.OnMessage.AddListener((peer, ch, data, reliable) =>
Debug.Log(System.Text.Encoding.UTF8.GetString(data)));
net.Host(); // or net.Join(ticketOrConnectCode)
}
}
// Note: write Peerova.Event / Peerova.EventType in full — UnityEngine has types with the same names.Godot 4
preview API SDK đang ở bản preview và có thể thay đổi trước bản 1.0. CLI đã ổn định.The SDK API is a preview and may change before 1.0. The CLI is stable.
Chép addons/peerova vào project (.NET), native lib vào addons/peerova/bin/<os>/, bật plugin, thêm node PeerovaNode.Copy addons/peerova into a .NET project, native libs into addons/peerova/bin/<os>/, enable the plugin and add a PeerovaNode.
# addons/peerova (Godot 4.2+ .NET project) — add a PeerovaNode to the scene
@onready var net = $PeerovaNode
func _ready():
net.connect("Connected", func(peer): net.SendText(peer, "hi", 0, true))
net.connect("Message", func(peer, ch, data, reliable): print(data.get_string_from_utf8()))
net.Join(ticket_or_connect_code) # or net.Host()Unreal / C / C++
preview API SDK đang ở bản preview và có thể thay đổi trước bản 1.0. CLI đã ổn định.The SDK API is a preview and may change before 1.0. The CLI is stable.
Plugin unreal/Peerova với UPeerovaSubsystem; hoặc gọi trực tiếp C ABI:The unreal/Peerova plugin with UPeerovaSubsystem; or call the C ABI directly:
#include "peerova.h" /* libpeerova.so · peerova.dll · libpeerova.dylib */
#include <stdio.h>
int main(void) {
peerova_node* n = peerova_server_start("{\"port\":47000,\"license\":\"PVA-XXXXX-XXXXX-XXXXX-XXXXX\"}");
if (!n) { fprintf(stderr, "%s\n", peerova_last_error()); return 1; }
char ticket[1024];
peerova_ticket(n, ticket, sizeof ticket);
printf("peerova %s ticket: %s\n", peerova_version(), ticket);
peerova_event ev;
for (;;) {
if (peerova_poll_wait(n, &ev, 100) == 1 && ev.type == PEEROVA_EVENT_MESSAGE)
peerova_send(n, ev.peer, ev.channel, ev.data, ev.len, 1 /* reliable */);
}
peerova_free(n);
}Python
preview API SDK đang ở bản preview và có thể thay đổi trước bản 1.0. CLI đã ổn định.The SDK API is a preview and may change before 1.0. The CLI is stable.
import os, peerova # ctypes binding; library via PEEROVA_LIB if needed
srv = peerova.Server(port=47000, license=os.environ.get("PEEROVA_LICENSE", "PVA-XXXXX-XXXXX-XXXXX-XXXXX"),
app="prj_xxx", share={"name": "Demo API", "public": True, "max": 50})
print("ticket:", srv.ticket, "connect code:", srv.room_code)
for ev in srv.events(timeout=1.0):
if ev.type == peerova.CONNECTED:
print("peer", ev.peer, "connected")
elif ev.type == peerova.MESSAGE:
srv.send(ev.peer, ev.data, channel=ev.channel, reliable=True) # echoJava / Kotlin / Android
preview API SDK đang ở bản preview và có thể thay đổi trước bản 1.0. CLI đã ổn định.The SDK API is a preview and may change before 1.0. The CLI is stable.
java/build.sh tạo peerova.jar, libpeerova_jni; Android build JNI bằng NDK cho từng ABI và cần quyền INTERNET.java/build.sh builds peerova.jar and libpeerova_jni; on Android build the JNI lib with the NDK per ABI and add the INTERNET permission.
// java/ (JNI): peerova.jar + libpeerova_jni + libpeerova
try (Peerova.Server srv = Peerova.Server.start("{\"port\":47000}");
Peerova.Client cli = Peerova.Client.connect(srv.ticket())) {
Peerova.Event ev = cli.poll(10000); // CONNECTED
cli.send(ev.peer, "hello", 0, true);
Peerova.Event m = srv.poll(5000); // CONNECTED, then MESSAGE
}Go, Node, Deno, Bun, Dart, Swift, Ruby, PHP, Lua…Go, Node, Deno, Bun, Dart, Swift, Ruby, PHP, Lua…
preview API SDK đang ở bản preview và có thể thay đổi trước bản 1.0. CLI đã ổn định.The SDK API is a preview and may change before 1.0. The CLI is stable.
// go get github.com/peerova/peerova-go (cgo, links libpeerova)
srv, _ := peerova.StartServer(`{"port":47000}`)
defer srv.Close()
cli, _ := peerova.Connect(srv.Ticket(), "")
ev, _ := cli.PollWait(10 * time.Second) // Connected
cli.Send(ev.Peer, []byte("hello"), 0, true)
Node/Electron/PHP không ext/Lua thuần/shell dùng sidecar: spawn peerova stdio server|client và trao đổi JSON lines — không cần native module.Node/Electron, PHP without ext/ffi, plain Lua and shells use the sidecar: spawn peerova stdio server|client and exchange JSON lines — no native module needed.
import { Peerova } from "peerova"; // bindings/nodejs (sidecar)
const srv = await Peerova.server({ port: 47000 });
srv.on("message", (peer, data) => console.log(data.toString()));
const cli = await Peerova.client(srv.ticket);
cli.on("connected", (peer) => cli.send(peer, "hello", { channel: 0, reliable: true }));
So sánh: mở port / NAT truyền thống vs relay vs PeerovaCompare: port forwarding vs relay vs Peerova
Bản gốc: docs/COMPARISON.md. Có ô cảnh báo — chúng tôi nói rõ giới hạn.Source: docs/COMPARISON.md. Some cells are caveats — we state the limits plainly.
| Mở port truyền thốngPort forwarding | Relay | Peerova | |
|---|---|---|---|
| Cần cấu hình routerRouter configuration | Có, phải forward tayYes, manual forwarding | KhôngNo | Không: tự dùng UPnP/NAT-PMP, IPv6 hoặc hole punchNo: automatic UPnP/NAT-PMP, IPv6 or hole punching |
| Port lộ ra InternetPort exposed to the Internet | Mở công khai, ai cũng quét thấyPublic; any scanner sees it | Không (server nằm sau relay)No (server sits behind the relay) | Dark: không trả lời gói thiếu ticket, quét port không thấy gìDark: no reply without a ticket; scans see nothing |
| Chạy sau CGNAT (4G, nhiều gói cáp quang)Works behind CGNAT (4G, many fibre plans) | KhôngNo | CóYes | Được nếu có IPv6 hoặc phía server reachable; không được nếu cả hai bên đều symmetric NAT/CGNAT và không có IPv6Yes with IPv6 or a reachable server side; no if both sides are symmetric NAT/CGNAT without IPv6 |
| Tỷ lệ kết nốiConnection success | ~100% nếu mở port được~100% if you can open a port | ~100%~100% | Ước tính 85–95%; gần 100% với dedicated server bạn kiểm soát mạngEstimated 85–95%; near 100% for dedicated servers whose network you control |
| Độ trễLatency | Thấp nhất (đường thẳng)Lowest (direct path) | +10–80 ms (đi vòng qua relay)+10–80 ms (detour via relay) | Thấp nhất (đường thẳng)Lowest (direct path) |
| Chi phí vận hànhRunning cost | 00 | Theo CCU / băng thôngPer CCU / bandwidth | ≈ 0 băng thông; chỉ thuê license≈ 0 bandwidth; license only |
| Băng thông tối đaMax bandwidth | Băng thông thậtYour real bandwidth | Bị giới hạn bởi relayCapped by the relay | Băng thông thật (982 Mbit/s đo trên loopback)Your real bandwidth (982 Mbit/s on loopback) |
| Mã hoáEncryption | Tuỳ game (thường không)Up to the game (often none) | Thường chỉ tới relayOften only up to the relay | E2E Noise IK (X25519 / ChaCha20-Poly1305)E2E Noise IK (X25519 / ChaCha20-Poly1305) |
| Chống MITM / giả serverMITM / fake server protection | KhôngNo | Phụ thuộc relayDepends on the relay | Có: ticket chứa public key serverYes: the ticket pins the server public key |
| Ẩn IP serverServer IP hidden | Lộ hoàn toànFully exposed | Ẩn sau relayHidden behind the relay | Một phần: ticket riêng không chứa IP; IP chỉ lộ cho client đã có ticket và đã xác thựcPartly: private ticket has no IP; only authenticated ticket holders learn it |
| Chống DDoS dung lượng lớnVolumetric DDoS | KhôngNo | Relay hấp thụAbsorbed by the relay | Không hấp thụ được nếu kẻ tấn công đã được cấp quyền và biết IP; người lạ không thấy serverCannot absorb it from an authorized client who knows the IP; strangers cannot see the server |
| Đổi mạng giữa trận (Wi-Fi → 4G)Network change mid-match (Wi-Fi → 4G) | RớtDrops | TuỳVaries | Roaming: tự cập nhật địa chỉ theo gói đã xác thựcRoaming: address updated from authenticated packets |
| Phụ thuộc bên thứ 3Third-party dependency | KhôngNo | Có (dịch vụ relay)Yes (relay service) | Không (web chỉ cấp license; offline vẫn chạy 7 ngày)No (web only issues licenses; runs 7 days offline) |
| Giao thứcProtocols | MọiAny | Thường chỉ SDK riêngOften the vendor SDK only | Mọi: TCP (HTTP/HTTPS/WS/SSH…) + UDP (game, QUIC, voice) + SDKAny: TCP (HTTP/HTTPS/WS/SSH…) + UDP (game, QUIC, voice) + SDK |
Hạn chế thật của P2P / NAT traversalReal limits of P2P / NAT traversal
| Hạn chếLimit | Nguyên nhânCause | Peerova xử lýHow Peerova handles it |
|---|---|---|
| Symmetric NAT ↔ symmetric NAT không punch đượcSymmetric ↔ symmetric NAT cannot be punched | Mỗi đích có port ngoài khác nhau, không đoán đượcEach destination gets an unpredictable external port | Ưu tiên IPv6; phía server bật UPnP/NAT-PMP hoặc mở 1 port UDP (dark nên an toàn); port prediction ở roadmapPrefer IPv6; enable UPnP/NAT-PMP on the server or open one UDP port (safe, it is dark); port prediction on the roadmap |
| CGNAT (100.64.0.0/10)CGNAT (100.64.0.0/10) | Nhà mạng NAT thêm một lớp, UPnP vô dụngThe ISP adds a NAT layer; UPnP is useless | IPv6, hoặc xin IP public (peerova doctor tự phát hiện)IPv6, or ask for a public IP (peerova doctor detects it) |
| Firewall doanh nghiệp chặn UDPCorporate firewall blocks UDP | Chính sách mạngNetwork policy | Chưa xử lý được: không relay, không TCP fallback — giới hạn của lựa chọn "0 relay"Not handled: no relay, no TCP fallback — the cost of "zero relay" |
| IP server lộ cho client hợp lệServer IP visible to valid clients | Kết nối trực tiếp thì gói phải mang IP thậtDirect packets must carry the real IP | Private ticket, token/allow-list, giới hạn CCU, rate-limit handshakePrivate ticket, token/allow-list, CCU limits, handshake rate limiting |
| Kết nối ban đầu chậm hơnSlower initial connect | Thu thập địa chỉ → punch → bắt tayGather addresses → punch → handshake | Thường vài chục ms tới 1–2 s, sau đó như kết nối thẳngTypically tens of ms to 1–2 s, then like a direct link |
| Cần điểm hẹn trao đổi địa chỉNeeds a rendezvous | NAT ẩn địa chỉ thậtNAT hides real addresses | Supernode (server khác của hệ thống), ticket, join code thủ côngSupernodes (other Peerova servers), tickets, manual join code |
Vì sao dedicated server hợp với PeerovaWhy dedicated servers fit Peerova
Hole punching chỉ thất bại khi cả hai đầu đều ở NAT khó. Khi bạn kiểm soát mạng phía server (IPv6, UPnP hoặc mở 1 port UDP — vẫn dark) thì server reachable và mọi client kết nối được.Hole punching only fails when both ends are behind hard NATs. When you control the server network (IPv6, UPnP, or one UDP port — still dark) the server is reachable and every client connects.
Xử lý sự cốTroubleshooting
CGNAT
Nếu IP WAN trên router khác IP public (thường là 100.64.x.x), bạn đang sau CGNAT: UPnP vô dụng. Giải pháp tốt nhất là bật IPv6; nếu không, hole punch vẫn thường thành công khi phía kia dễ tính.If your router's WAN IP differs from your public IP (often 100.64.x.x), you are behind CGNAT: UPnP will not help. The best fix is enabling IPv6; otherwise hole punching often still works when the other side is friendly.
Double NAT
Modem nhà mạng + router riêng = hai lớp NAT. Chuyển modem sang chế độ bridge, hoặc bật UPnP ở cả hai, hoặc đặt router riêng vào DMZ của modem.ISP modem + your own router = two NAT layers. Put the modem in bridge mode, enable UPnP on both, or put your router in the modem's DMZ.
Bật IPv6Enable IPv6
Vào trang quản trị router → WAN/IPv6 → bật IPv6 (SLAAC/DHCPv6, prefix delegation). Viettel, VNPT, FPT đều hỗ trợ. Kiểm tra lại bằng peerova doctor.Router admin → WAN/IPv6 → enable IPv6 (SLAAC/DHCPv6, prefix delegation). Most ISPs support it. Re-check with peerova doctor.
UPnP
Bật UPnP-IGD hoặc NAT-PMP trong router. Peerova chỉ xin mapping tạm thời và tự gỡ khi tắt.Enable UPnP-IGD or NAT-PMP on the router. Peerova only requests a temporary mapping and removes it on exit.
Firewall hệ điều hànhOS firewall
Cho phép peerova nhận UDP trên Windows Defender Firewall / ufw. Không cần mở cổng trên router.Allow peerova to receive UDP in Windows Defender Firewall / ufw. No router port needed.
Vẫn không được?Still stuck?
Dùng mã join thủ công (pvj_…) và gửi kết quả peerova doctor của cả hai máy cho chúng tôi.Use the manual join code (pvj_…) and send us the peerova doctor output from both machines.