Files
p2ns/docs/OPENAPI.yaml
T
Raven Scott 480f99ae28 feat: allow partial domain removal and add conflict status
- Add partial domain removal functionality allowing users to remove their own claims and votes even when they're not the resolved claimant
- Add conflict status detection for domains where local peer has claim but another claimant won consensus
- Update UI to display conflict status with red badge
- Enhance API responses with detailed consensus information
- Update all documentation with new features

Changes:
- core/core.js: Add removeOwnClaimAndVotes() function for partial removal
- admin/routes/domains.js: Implement partial/full removal logic in /api/remove-domain
- admin/admin-backend/routes/domains.js: Same partial removal logic for backend routes
- plugins/sdk.js: Update removeDomain() to support partial removal
- admin/ui/config.js: Add conflict status badge and update postFetch
- admin/admin-frontend/ui/config.js: Add conflict status badge and fix status rendering
- admin/utils.js: Add conflict status to utility functions
- admin/admin-frontend/utils.js: Add conflict status to utility functions
- docs/CONSENSUS.md: Add conflict status to resolution states
- docs/README_LONGFORM.md: Add conflict status to consensus states
- docs/RESTAPI.md: Update /api/resolved-domains with new fields and examples
- docs/ARCHITECTURE.md: Document full vs partial removal modes
- docs/OPENAPI.yaml: Update Domain schema and add ConsensusState schema
2025-12-26 15:32:41 -05:00

1410 lines
35 KiB
YAML

openapi: 3.0.3
info:
title: P2NS Admin API
description: |
REST API for managing the P2NS (Peer-to-Peer Name System) decentralized DNS system.
Provides endpoints for domain management, Holesail servers/clients, local DNS records,
certificates, virtual interfaces, and system monitoring.
version: 1.0.0
contact:
name: P2NS Support
url: https://git.ssh.surf/snxraven/p2ns
servers:
- url: https://p2ns.admin
description: P2NS Admin Interface
tags:
- name: System
description: System status and health endpoints
- name: Domains
description: Domain management endpoints
- name: Holesail
description: Holesail server and client management
- name: DNS
description: Local DNS record management
- name: Certificates
description: Certificate management
- name: Interfaces
description: Virtual interface management
- name: Settings
description: Configuration management
- name: Backups
description: Backup and restore operations
- name: Diagnostics
description: Network diagnostics and testing tools
- name: Metrics
description: System metrics and monitoring
- name: Consensus
description: Consensus and voting endpoints
- name: Subscriptions
description: Service subscription management endpoints
paths:
/api/health:
get:
tags:
- System
summary: Health check endpoint
description: |
Returns system health status. Supports liveness and readiness probes.
- Liveness: Checks if process is alive
- Readiness: Checks if services are ready to accept traffic
parameters:
- name: probe
in: query
schema:
type: string
enum: [liveness, readiness]
default: liveness
description: Type of health probe
responses:
'200':
description: System is healthy
content:
application/json:
schema:
$ref: '#/components/schemas/HealthStatus'
'503':
description: System is degraded or not ready
content:
application/json:
schema:
$ref: '#/components/schemas/HealthStatus'
/api/status:
get:
tags:
- System
summary: Get system status
responses:
'200':
description: System status
content:
application/json:
schema:
$ref: '#/components/schemas/Status'
/api/stats:
get:
tags:
- Metrics
summary: Get system metrics
responses:
'200':
description: System metrics
content:
application/json:
schema:
$ref: '#/components/schemas/Metrics'
/api/stats/historical:
get:
tags:
- Metrics
summary: Get historical metrics
parameters:
- name: minutes
in: query
schema:
type: integer
minimum: 1
maximum: 1440
default: 60
description: Time range in minutes
responses:
'200':
description: Historical metrics data
content:
application/json:
schema:
$ref: '#/components/schemas/HistoricalMetrics'
/api/resolved-domains:
get:
tags:
- Domains
summary: List all resolved domains
responses:
'200':
description: List of domains
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Domain'
/api/add-domain:
post:
tags:
- Domains
summary: Add a domain
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/AddDomainRequest'
responses:
'200':
description: Domain added successfully
content:
text/plain:
schema:
type: string
example: OK
'500':
description: Error adding domain
/api/remove-domain:
post:
tags:
- Domains
summary: Remove a domain
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/RemoveDomainRequest'
responses:
'200':
description: Domain removed successfully
content:
text/plain:
schema:
type: string
example: OK
'500':
description: Error removing domain
/api/holesail-servers:
get:
tags:
- Holesail
summary: List Holesail servers
responses:
'200':
description: List of servers
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/HolesailServer'
/api/holesail-create:
post:
tags:
- Holesail
summary: Create a Holesail server
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateHolesailServerRequest'
responses:
'200':
description: Server created
content:
application/json:
schema:
type: object
properties:
id:
type: string
/api/holesail-client-create:
post:
tags:
- Holesail
summary: Create a Holesail client
description: Creates a new Holesail client for a domain and port. If serviceName is provided, the client ID will be domain_servicename. The client is added to the domain's claim record clients array.
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateHolesailClientRequest'
responses:
'200':
description: Client created
content:
application/json:
schema:
type: object
properties:
id:
type: string
'400':
description: Invalid request (missing required fields or domain not owned)
'500':
description: Failed to create Holesail client
/api/local-dns:
get:
tags:
- DNS
summary: Get local DNS records and conflicts
responses:
'200':
description: DNS records and conflicts
content:
application/json:
schema:
$ref: '#/components/schemas/LocalDNS'
/api/add-local-dns:
post:
tags:
- DNS
summary: Add a local DNS record
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/DNSRecord'
responses:
'200':
description: Record added
content:
text/plain:
schema:
type: string
example: OK
/api/certs:
get:
tags:
- Certificates
summary: List certificates
responses:
'200':
description: List of domains with certificates
content:
application/json:
schema:
type: array
items:
type: string
/api/generate-cert:
post:
tags:
- Certificates
summary: Generate a certificate
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
domain:
type: string
responses:
'200':
description: Certificate generated
content:
text/plain:
schema:
type: string
example: OK
/api/backups:
get:
tags:
- Backups
summary: List all backups
responses:
'200':
description: List of backups
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Backup'
/api/backups/create:
post:
tags:
- Backups
summary: Create manual backup
responses:
'200':
description: Backup created
content:
application/json:
schema:
$ref: '#/components/schemas/BackupCreateResponse'
/api/backups/restore:
post:
tags:
- Backups
summary: Restore from backup
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/RestoreBackupRequest'
responses:
'200':
description: Backup restored
content:
application/json:
schema:
$ref: '#/components/schemas/BackupRestoreResponse'
/api/backups/{id}:
delete:
tags:
- Backups
summary: Delete backup
parameters:
- name: id
in: path
required: true
schema:
type: string
description: Backup name
responses:
'200':
description: Backup deleted
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
message:
type: string
/api/network-interfaces:
get:
tags:
- Settings
summary: Get available network interfaces
description: |
Retrieves a list of available network interfaces on the system.
Used by the admin interface to populate the Subnet Interface Name dropdown setting.
Interfaces are sorted with loopback interfaces and OS-specific defaults prioritized first.
responses:
'200':
description: List of available network interfaces
content:
application/json:
schema:
type: object
properties:
interfaces:
type: array
items:
type: object
properties:
value:
type: string
description: Interface name (e.g., "lo0", "lo", "eth0")
example: "lo0"
label:
type: string
description: Display label for the interface (may include "(default)" for OS default)
example: "lo0"
'500':
description: Failed to fetch network interfaces
content:
application/json:
schema:
type: object
properties:
error:
type: string
/api/backups/{id}/metadata:
get:
tags:
- Backups
summary: Get backup metadata
parameters:
- name: id
in: path
required: true
schema:
type: string
description: Backup name
responses:
'200':
description: Backup metadata
content:
application/json:
schema:
$ref: '#/components/schemas/BackupMetadata'
'404':
description: Backup not found
/api/diagnostics/dns-lookup:
post:
tags:
- Diagnostics
summary: DNS lookup diagnostic
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/DNSLookupRequest'
responses:
'200':
description: DNS lookup result
content:
application/json:
schema:
$ref: '#/components/schemas/DNSLookupResponse'
/api/diagnostics/ping:
post:
tags:
- Diagnostics
summary: Ping diagnostic
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/PingRequest'
responses:
'200':
description: Ping result (non-streaming) or NDJSON stream (streaming)
content:
application/json:
schema:
$ref: '#/components/schemas/PingResponse'
application/x-ndjson:
schema:
$ref: '#/components/schemas/PingStreamResponse'
/api/diagnostics/traceroute:
post:
tags:
- Diagnostics
summary: Traceroute diagnostic
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/TracerouteRequest'
responses:
'200':
description: Traceroute result (non-streaming) or NDJSON stream (streaming)
content:
application/json:
schema:
$ref: '#/components/schemas/TracerouteResponse'
application/x-ndjson:
schema:
$ref: '#/components/schemas/TracerouteStreamResponse'
/api/diagnostics/connection-test:
post:
tags:
- Diagnostics
summary: Connection test
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ConnectionTestRequest'
responses:
'200':
description: Connection test result
content:
application/json:
schema:
$ref: '#/components/schemas/ConnectionTestResponse'
/api/diagnostics/bandwidth:
get:
tags:
- Diagnostics
summary: Network interface bandwidth information
responses:
'200':
description: Network interface information
content:
application/json:
schema:
$ref: '#/components/schemas/BandwidthResponse'
/api/consensus/{domain}:
get:
tags:
- Consensus
summary: Get consensus state for a domain
parameters:
- name: domain
in: path
required: true
schema:
type: string
description: The domain name
responses:
'200':
description: Consensus state
content:
application/json:
schema:
$ref: '#/components/schemas/ConsensusState'
'500':
description: Failed to get consensus state
/api/consensus/metrics:
get:
tags:
- Consensus
summary: Get consensus metrics
responses:
'200':
description: Consensus metrics
content:
application/json:
schema:
$ref: '#/components/schemas/ConsensusMetrics'
'500':
description: Failed to get consensus metrics
/api/consensus/recalculate:
post:
tags:
- Consensus
summary: Force consensus recalculation for all domains
responses:
'200':
description: Recalculation triggered
content:
application/json:
schema:
$ref: '#/components/schemas/RecalculateResponse'
'500':
description: Failed to recalculate consensus
/api/consensus/recalculate/{domain}:
post:
tags:
- Consensus
summary: Force consensus recalculation for a specific domain
parameters:
- name: domain
in: path
required: true
schema:
type: string
description: The domain name
responses:
'200':
description: Recalculation triggered
content:
application/json:
schema:
$ref: '#/components/schemas/RecalculateResponse'
'500':
description: Failed to recalculate consensus
/api/domain-services:
get:
tags:
- Subscriptions
summary: Get services for a domain
parameters:
- name: domain
in: query
required: true
schema:
type: string
description: The domain name
responses:
'200':
description: List of services for the domain
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/DomainService'
'400':
description: Missing domain parameter
'500':
description: Failed to fetch domain services
/api/service-subscriptions:
get:
tags:
- Subscriptions
summary: List all service subscriptions
responses:
'200':
description: List of subscriptions
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/ServiceSubscription'
'500':
description: Failed to fetch subscriptions
/api/subscribe-all-domains:
get:
tags:
- Subscriptions
summary: List domains with subscribe-all enabled
responses:
'200':
description: List of domains
content:
application/json:
schema:
type: array
items:
type: string
'500':
description: Failed to fetch subscribe-all domains
/api/service-subscribe:
post:
tags:
- Subscriptions
summary: Subscribe to a service
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ServiceSubscribeRequest'
responses:
'200':
description: Successfully subscribed
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
'400':
description: Missing required fields
'409':
description: Already subscribed to this service
'500':
description: Failed to subscribe
/api/service-unsubscribe:
post:
tags:
- Subscriptions
summary: Unsubscribe from a service
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ServiceUnsubscribeRequest'
responses:
'200':
description: Successfully unsubscribed
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
'400':
description: Missing required fields
'404':
description: Subscription not found
'500':
description: Failed to unsubscribe
/api/subscribe-all:
post:
tags:
- Subscriptions
summary: Enable subscribe-all for a domain
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/SubscribeAllRequest'
responses:
'200':
description: Successfully enabled subscribe-all
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
'400':
description: Missing domain field
'500':
description: Failed to set subscribe-all
/api/unsubscribe-all:
post:
tags:
- Subscriptions
summary: Disable subscribe-all for a domain
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/SubscribeAllRequest'
responses:
'200':
description: Successfully disabled subscribe-all
content:
application/json:
schema:
type: object
properties:
success:
type: boolean
'400':
description: Missing domain field
'500':
description: Failed to clear subscribe-all
components:
schemas:
HealthStatus:
type: object
properties:
status:
type: string
enum: [healthy, degraded, not_ready]
timestamp:
type: string
format: date-time
uptime:
type: integer
probe:
type: string
services:
type: object
dependencies:
type: object
Status:
type: object
properties:
isMaster:
type: boolean
isConnected:
type: boolean
peersCount:
type: integer
Domain:
type: object
properties:
domain:
type: string
hash:
type: string
isLocal:
type: boolean
description: Whether the local writer has a claim for this domain
isOwner:
type: boolean
description: Whether the local writer is the resolved claimant (owner) of this domain
consensusState:
$ref: '#/components/schemas/ConsensusState'
description: Detailed consensus information for this domain
consensusStatus:
type: string
enum: [resolved, conflict, insufficient_quorum, tie, no_claims, error, internal]
description: Simplified consensus status, may include 'conflict' for domains where local peer has claim but another claimant won
services:
type: array
items:
$ref: '#/components/schemas/DomainService'
description: Array of services configured for this domain
AddDomainRequest:
type: object
required:
- domain
- hash
properties:
domain:
type: string
description: Domain name to add
hash:
type: string
description: Holesail connection hash (e.g., hs://...)
ssl:
type: boolean
default: false
description: Whether the Holesail connection uses SSL/TLS. When true, proxy connections will use HTTPS/WSS instead of HTTP/WS.
RemoveDomainRequest:
type: object
required:
- domain
properties:
domain:
type: string
HolesailServer:
type: object
properties:
id:
type: string
opts:
type: object
info:
type: object
CreateHolesailServerRequest:
type: object
properties:
name:
type: string
port:
type: integer
host:
type: string
secure:
type: boolean
udp:
type: boolean
log:
type: integer
domain:
type: string
CreateHolesailClientRequest:
type: object
required:
- domain
- key
- port
properties:
domain:
type: string
description: Domain name (must be owned by local writer)
key:
type: string
description: Holesail connection hash (e.g., hs://s00084bf...)
port:
type: integer
description: Port number
serviceName:
type: string
description: Service name for naming the client (format: domain_servicename)
protocol:
type: string
enum: [tcp, udp]
default: tcp
description: Protocol type
LocalDNS:
type: object
properties:
records:
type: array
items:
type: object
conflicts:
type: array
items:
type: object
DNSRecord:
type: object
properties:
name:
type: string
type:
type: string
ttl:
type: integer
data:
type: string
Metrics:
type: object
description: Comprehensive system metrics
properties:
requests:
type: object
properties:
total:
type: integer
successful:
type: integer
failed:
type: integer
successRate:
type: number
avgResponseTime:
type: number
holesailChildren:
type: array
items:
type: object
HistoricalMetrics:
type: object
description: Historical metrics data
properties:
timestamp:
type: string
format: date-time
data:
type: object
Backup:
type: object
properties:
name:
type: string
timestamp:
type: string
format: date-time
size:
type: integer
sizeFormatted:
type: string
fileCount:
type: integer
BackupCreateResponse:
type: object
properties:
success:
type: boolean
path:
type: string
RestoreBackupRequest:
type: object
required:
- backupName
properties:
backupName:
type: string
BackupRestoreResponse:
type: object
properties:
success:
type: boolean
message:
type: string
BackupMetadata:
type: object
properties:
timestamp:
type: string
format: date-time
files:
type: array
items:
type: object
properties:
name:
type: string
size:
type: integer
sizeFormatted:
type: string
modified:
type: string
format: date-time
DNSLookupRequest:
type: object
required:
- domain
properties:
domain:
type: string
type:
type: string
enum: [A, AAAA, MX, TXT, NS, CNAME, SRV, PTR, SOA]
default: A
DNSLookupResponse:
type: object
properties:
success:
type: boolean
domain:
type: string
type:
type: string
results:
type: array
items:
type: string
responseTime:
type: integer
PingRequest:
type: object
required:
- target
properties:
target:
type: string
count:
type: integer
default: 4
stream:
type: boolean
default: false
PingResponse:
type: object
properties:
success:
type: boolean
target:
type: string
count:
type: integer
output:
type: string
error:
type: string
responseTime:
type: integer
PingStreamResponse:
type: object
properties:
type:
type: string
enum: [output, error, complete]
data:
type: string
timestamp:
type: integer
success:
type: boolean
exitCode:
type: integer
TracerouteRequest:
type: object
required:
- target
properties:
target:
type: string
stream:
type: boolean
default: false
TracerouteResponse:
type: object
properties:
success:
type: boolean
target:
type: string
output:
type: string
error:
type: string
responseTime:
type: integer
TracerouteStreamResponse:
type: object
properties:
type:
type: string
enum: [output, error, complete]
data:
type: string
timestamp:
type: integer
success:
type: boolean
exitCode:
type: integer
ConnectionTestRequest:
type: object
required:
- domain
- port
properties:
domain:
type: string
port:
type: integer
ConnectionTestResponse:
type: object
properties:
success:
type: boolean
domain:
type: string
ip:
type: string
port:
type: integer
latency:
type: integer
error:
type: string
responseTime:
type: integer
BandwidthResponse:
type: object
properties:
interfaces:
type: object
additionalProperties:
type: object
properties:
name:
type: string
addresses:
type: array
items:
type: object
note:
type: string
responseTime:
type: integer
ConsensusState:
type: object
description: Consensus state for a domain
properties:
status:
type: string
enum: [resolved, insufficient_quorum, tie, no_claims, error]
description: Consensus status
hash:
type: string
nullable: true
description: Resolved Holesail hash if status is resolved
resolvedClaimant:
type: string
nullable: true
description: Public key of winning claimant if resolved
voteCounts:
type: object
additionalProperties:
type: integer
description: Mapping of claimant public keys to vote counts
activePeers:
type: integer
description: Number of active peers in network (including local node)
quorumMet:
type: boolean
description: Whether quorum threshold has been met
minVotes:
type: integer
description: Minimum votes required to meet quorum
totalVotes:
type: integer
description: Total number of votes cast for all claimants
lastResolution:
type: integer
nullable: true
description: Timestamp of last resolution, or null if never resolved
error:
type: string
description: Error message if status is error
ConsensusState:
type: object
description: Detailed consensus state for a specific domain
properties:
status:
type: string
enum: [resolved, conflict, insufficient_quorum, tie, no_claims, error]
description: Current consensus status
hash:
type: string
description: Resolved hash for the domain (null if not resolved)
resolvedClaimant:
type: string
description: Public key of the resolved claimant (null if not resolved)
voteCounts:
type: object
additionalProperties:
type: integer
description: Vote counts per claimant (claimant -> vote count)
activePeers:
type: integer
description: Number of active peers in the network
quorumMet:
type: boolean
description: Whether quorum requirements were met
minVotes:
type: integer
description: Minimum votes required for quorum
totalVotes:
type: integer
description: Total votes cast for this domain
lastResolution:
type: integer
description: Timestamp of last resolution attempt
ConsensusMetrics:
type: object
description: Overall consensus metrics
properties:
resolutions:
type: integer
description: Total number of successful domain resolutions
quorumFailures:
type: integer
description: Number of times quorum was not met
ties:
type: integer
description: Number of ties that required tie-breaking
validationFailures:
type: integer
description: Number of invalid votes that were rejected
totalVotes:
type: integer
description: Total number of votes cast across all domains
avgVotesPerDomain:
type: number
description: Average number of votes per domain
domainResolutions:
type: array
items:
type: object
properties:
domain:
type: string
resolved:
type: integer
description: Number of successful resolutions for this domain
failed:
type: integer
description: Number of failed resolutions for this domain
description: Per-domain resolution statistics
RecalculateResponse:
type: object
description: Response from consensus recalculation request
properties:
success:
type: boolean
description: Whether recalculation was triggered successfully
message:
type: string
description: Status message
DomainService:
type: object
description: Service information for a domain
properties:
serviceName:
type: string
description: Unique identifier for the service
key:
type: string
description: Holesail connection hash
port:
type: integer
description: Port number for the service
protocol:
type: string
enum: [tcp, udp]
description: Protocol type
ServiceSubscription:
type: object
description: Service subscription information
properties:
domain:
type: string
description: Domain name
serviceName:
type: string
description: Service identifier
key:
type: string
description: Holesail connection hash
port:
type: integer
description: Port number
protocol:
type: string
enum: [tcp, udp]
description: Protocol type
ServiceSubscribeRequest:
type: object
required:
- domain
- serviceName
- key
- port
properties:
domain:
type: string
description: Domain name
serviceName:
type: string
description: Service identifier
key:
type: string
description: Holesail connection hash
port:
type: integer
description: Port number
protocol:
type: string
enum: [tcp, udp]
default: tcp
description: Protocol type
ServiceUnsubscribeRequest:
type: object
required:
- domain
- serviceName
properties:
domain:
type: string
description: Domain name
serviceName:
type: string
description: Service identifier
SubscribeAllRequest:
type: object
required:
- domain
properties:
domain:
type: string
description: Domain name