octaspace

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 15, 2026 License: MIT Imports: 14 Imported by: 0

README

OctaSpace Go SDK

CI Go Reference Go Report Card

Go client library for the OctaSpace API — a decentralized marketplace for GPU compute, VPN relays, and rendering.

  • Zero external dependencies (standard library only)
  • Resource-oriented API: client.Nodes.List(ctx, nil), client.Services.MR.Create(ctx, params)
  • Typed error hierarchy with errors.As support
  • Automatic retry with exponential backoff on transient errors (429, 5xx)
  • Multi-URL failover — automatically switches to a backup host on network-level errors
  • context.Context support on every method — timeouts and cancellation work out of the box

Requirements

Go 1.20 or later.

Installation

go get github.com/octaspace/go-sdk

Quick start

package main

import (
    "context"
    "fmt"
    "os"

    octaspace "github.com/octaspace/go-sdk"
)

func main() {
    ctx := context.Background()
    client := octaspace.NewClient(os.Getenv("OCTA_API_KEY"))

    // Public endpoint — no API key required
    info, err := client.Network.Info(ctx)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
    // info["nodes"] is an object like {"count":690,"vpn":147,...}, not an integer
    fmt.Printf("Network info: %v\n", info)

    // Authenticated endpoint
    account, err := client.Accounts.Profile(ctx)
    if err != nil {
        fmt.Fprintln(os.Stderr, err)
        os.Exit(1)
    }
    fmt.Printf("Logged in as %s\n", account.Email)
}

Creating a client

// With API key — access to all endpoints
client := octaspace.NewClient(os.Getenv("OCTA_API_KEY"))

// Without API key — only public endpoints (e.g. Network.Info)
client := octaspace.NewClient("")

The API key is sent as the Authorization header. When the key is empty the header is omitted entirely — public endpoints respond normally, authenticated endpoints return HTTP 401.

Options

All options are passed as variadic arguments to NewClient:

client := octaspace.NewClient(apiKey,
    octaspace.WithBaseURL("https://api.octa.space"), // default
    octaspace.WithTimeout(15*time.Second),            // per-request timeout (default: 30s)
    octaspace.WithMaxRetries(3),                      // retries on 429/5xx (default: 2)
    octaspace.WithLogger(myLogger),                   // request/response logging
    octaspace.WithHTTPClient(myHTTPClient),           // custom http.Client
    octaspace.WithUserAgent("my-app/1.0"),            // override User-Agent
)
Logging

WithLogger accepts any value that satisfies the Logger interface:

type Logger interface {
    Debugf(format string, v ...any)
    Errorf(format string, v ...any)
}

The standard library's *log.Logger does not satisfy this interface directly (it has Printf but not Debugf/Errorf). Use the provided NewStdLogger adapter:

import "log"

l := log.New(os.Stderr, "[octaspace] ", log.LstdFlags)
client := octaspace.NewClient(apiKey, octaspace.WithLogger(octaspace.NewStdLogger(l)))

Resources

The client is organized into resource groups that mirror the API structure. Every method takes context.Context as the first argument.

Network (public)
// GET /network — no authentication required
info, err := client.Network.Info(ctx)
// info is map[string]any — contains blockchain stats, marketplace summary, node counts, etc.
Accounts
// GET /accounts
account, err := client.Accounts.Profile(ctx)
// account.AccountUUID, account.Email, account.Wallet, account.Balance (BigInt)

// GET /accounts/balance — returns *big.Int denominated in Wei
balance, err := client.Accounts.Balance(ctx)
fmt.Printf("Balance: %s wei\n", balance.String())

// POST /accounts — generate a new wallet for the account
account, err := client.Accounts.GenerateWallet(ctx)
Nodes
// GET /nodes — all nodes
nodes, err := client.Nodes.List(ctx, nil)

// GET /nodes?country=US — filter by country ISO code
country := "US"
nodes, err := client.Nodes.List(ctx, &octaspace.ListNodesParams{Country: &country})

// GET /nodes?app_uuid=<uuid> — filter by compatible application
appUUID := "3f2a1..."
nodes, err := client.Nodes.List(ctx, &octaspace.ListNodesParams{AppUUID: &appUUID})

// GET /nodes/:id
node, err := client.Nodes.Find(ctx, 42)

// GET /nodes/:id/ident — returns identity file as []byte
identBytes, err := client.Nodes.DownloadIdent(ctx, 42)

// GET /nodes/:id/logs — returns log archive as []byte
logBytes, err := client.Nodes.DownloadLogs(ctx, 42)

// PATCH /nodes/:id/prices — update your node's pricing
err := client.Nodes.UpdatePrices(ctx, 42, map[string]float64{
    "gpu_hour": 0.5,
    "cpu_hour": 0.1,
})

// GET /nodes/:id/reboot
err := client.Nodes.Reboot(ctx, 42)

Node struct highlights:

type Node struct {
    ID       int64  // node identifier
    IP       string
    State    string // "idle", "busy", etc.
    OSN      string // OctaSpace Node agent version
    Uptime   int64  // seconds
    Location struct {
        City      string
        Country   string
        Latitude  float64
        Longitude float64
    }
    Prices struct {
        Base    int64 // Wei (node-owner-configured)
        Storage int64
        Traffic int64
    }
    System struct {
        CPUModelName   string
        CPUCores       int
        CPULoadPercent float64
        OSVersion      string
        Disk  struct{ Free, Size, Used int64; UsedPercent float64 }
        Memory struct{ Free, Size int64 }
        GPUs []struct {
            Model          string
            MemTotalMB     int
            GPUTemperature int
            GPUUtilization int
            // ... and more telemetry fields
        }
    }
}
Sessions

Sessions represent active or recent service deployments.

// GET /sessions — active sessions
sessions, err := client.Sessions.List(ctx, nil)

// GET /sessions?recent=true — include recently finished sessions
recent := true
sessions, err := client.Sessions.List(ctx, &octaspace.ListSessionsParams{Recent: &recent})

Note: The /sessions and /sessions?recent=true endpoints return overlapping but different field sets. The recent=true response includes FinishedAt, RX, TX, TerminationReason and returns Duration as a quoted string. The SDK handles both transparently via FlexInt64.

Services — Machine Rental (compute)
// GET /services/mr — available machines for rent
machines, err := client.Services.MR.List(ctx, nil)

// With query filters (raw url.Values)
import "net/url"
params := url.Values{"country": []string{"DE"}}
machines, err := client.Services.MR.List(ctx, params)

// POST /services/mr — create a machine rental session
resp, err := client.Services.MR.Create(ctx, &octaspace.MachineRentalCreateParams{
    NodeID:   42,
    DiskSize: 50,   // GB
    Image:    "ubuntu:22.04",
    App:      "",   // empty = custom image; or use App.UUID from Apps.List
    Envs: map[string]string{
        "JUPYTER_TOKEN": "mysecret",
    },
    Ports:        []int{8888},
    HTTPPorts:    []int{8080},
    StartCommand: "",
    Entrypoint:   "",
})
// resp.UUID is the session UUID — use it with Services.Session(uuid)

MachineRental pricing fields (all base_usd / storage_usd / traffic_usd are scaled ×10 000):

// base_usd: 1750 → $0.175/hr
pricePerHour := float64(m.BaseUSD) / 10_000

// Or use the pre-computed field:
fmt.Printf("$%.4f/hr\n", m.TotalPriceUSD)

Network speed fields on MachineRental and VPNRelay require conversion — raw values > 100 000 are in bytes/sec, not Mbit/s. Use the provided helpers:

fmt.Printf("Down: %.0f Mbps\n", machine.NetDownMbps())
fmt.Printf("Up:   %.0f Mbps\n", machine.NetUpMbps())
Services — VPN
// GET /services/vpn — available VPN relay nodes
relays, err := client.Services.VPN.List(ctx, nil)
for _, r := range relays {
    fmt.Printf("[%d] %s %s  ↓%.0f Mbps  $%.4f/GB\n",
        r.NodeID, r.Country, r.City, r.DownloadMbps(), r.PricePerGB)
}

// POST /services/vpn — create a VPN session
// SubKind: "wg" (WireGuard), "openvpn", "ss" (Shadowsocks), "v2ray"
resp, err := client.Services.VPN.Create(ctx, &octaspace.VPNCreateParams{
    NodeID:  247,
    SubKind: "wg",
})
// resp.UUID → use with Services.Session(uuid)

// v2ray requires the Protocol field:
resp, err = client.Services.VPN.Create(ctx, &octaspace.VPNCreateParams{
    NodeID:   247,
    SubKind:  "v2ray",
    Protocol: "vmess",
})
Services — Render
// GET /services/render — active/available render jobs
jobs, err := client.Services.Render.List(ctx, nil)
// Each job is map[string]any — schema varies

// POST /services/render
resp, err := client.Services.Render.Create(ctx, map[string]any{
    "node_id": 42,
    "image":   "blender:4.0",
})
Services — Per-session operations

Once you have a session UUID (from MR.Create, VPN.Create, etc.), use Services.Session(uuid) to operate on it:

proxy := client.Services.Session("550e8400-e29b-41d4-a716-446655440000")

// GET /services/:uuid/info
info, err := proxy.Info(ctx)
fmt.Println(info.VPNConfig) // WireGuard/OpenVPN config — paste into wg0.conf

// GET /services/:uuid/logs — active (current) session
logs, err := proxy.Logs(ctx, nil)

// GET /services/:uuid/logs?recent=true — finished (recent) session
recent := true
logs, err = proxy.Logs(ctx, &octaspace.LogsParams{Recent: &recent})

// POST /services/:uuid/stop
err := proxy.Stop(ctx, nil)

// Stop with a quality score (1–5)
score := 5
err := proxy.Stop(ctx, &octaspace.StopParams{Score: &score})
Apps
// GET /apps — application catalog (GPU apps, OS images, mining software, etc.)
apps, err := client.Apps.List(ctx)
for _, a := range apps {
    fmt.Printf("%-30s  category=%-15s  image=%s\n", a.Name, a.Category, a.Image)
}
// Use a.UUID when creating machine rental sessions with a specific application
Idle Jobs
// GET /idle_jobs/:node_id/:job_id
job, err := client.IdleJobs.Find(ctx, nodeID, jobID)

// GET /idle_jobs/:node_id/:job_id/logs — returns raw bytes
logBytes, err := client.IdleJobs.Logs(ctx, nodeID, jobID)

Error handling

All errors are typed and form a hierarchy rooted at *APIError. Use errors.As to inspect them:

nodes, err := client.Nodes.List(ctx, nil)
if err != nil {
    var authErr *octaspace.AuthenticationError
    var notFound *octaspace.NotFoundError
    var rateLimit *octaspace.RateLimitError
    var serverErr *octaspace.ServerError
    var netErr *octaspace.NetworkError

    switch {
    case errors.As(err, &authErr):
        // HTTP 401 — check your API key
        fmt.Println("invalid or expired API key")
    case errors.As(err, &notFound):
        // HTTP 404
        fmt.Println("resource not found")
    case errors.As(err, &rateLimit):
        // HTTP 429 — RetryAfter is populated when the server sends Retry-After header
        fmt.Printf("rate limited, retry after %ds\n", rateLimit.RetryAfter)
    case errors.As(err, &serverErr):
        // HTTP 5xx
        fmt.Printf("server error (request-id: %s)\n", serverErr.RequestID)
    case errors.As(err, &netErr):
        // transport failure (DNS, TLS, connection refused)
        fmt.Printf("network error: %v\n", netErr.Err)
    default:
        fmt.Println(err)
    }
}

Convenience helpers for the most common cases:

octaspace.IsAuthError(err)  // true for *AuthenticationError
octaspace.IsNotFound(err)   // true for *NotFoundError
octaspace.IsRateLimit(err)  // true for *RateLimitError

All error subtypes embed *APIError, so you can always unwrap to it:

var apiErr *octaspace.APIError
if errors.As(err, &apiErr) {
    fmt.Printf("HTTP %d, request-id: %s\n", apiErr.StatusCode, apiErr.RequestID)
}
Error types reference
Type HTTP status Description
*NetworkError Transport failure (DNS, TLS, timeout, refused)
*APIError any 4xx/5xx Base type; all subtypes embed it
*AuthenticationError 401 Invalid or missing API key
*PermissionError 403 API key lacks permission for this operation
*NotFoundError 404 Resource does not exist
*ValidationError 422 Request parameters failed server-side validation
*RateLimitError 429 Too many requests; check RetryAfter
*ServerError 5xx Internal server error

Multi-URL failover

Pass multiple base URLs to have the client automatically switch to the next one on network-level errors (connection refused, DNS failure, TLS error). HTTP errors (4xx, 5xx) are returned immediately without failover.

client := octaspace.NewClient(apiKey,
    octaspace.WithBaseURLs([]string{
        "https://api.octa.space",
        "https://api-backup.octa.space",
    }),
)

Failover is transparent to the caller — the error returned is from the last attempted host if all fail.

Retries

The client retries idempotent requests (GET) on transient errors (HTTP 429, 500, 502, 503, 504) using exponential backoff with ±25% jitter:

Attempt Base delay
1st retry 500ms
2nd retry 1s
3rd retry 2s

When the server includes a Retry-After header, its value is used instead.

POST and PATCH requests are never retried automatically.

Configure retry behaviour:

// disable retries entirely
client := octaspace.NewClient(apiKey, octaspace.WithMaxRetries(0))

// up to 5 retries
client := octaspace.NewClient(apiKey, octaspace.WithMaxRetries(5))

Special types

BigInt

Account balances are denominated in Wei and can exceed the range of int64 (~9.2×10¹⁸). The SDK uses BigInt, a thin wrapper around *big.Int, that marshals and unmarshals as a bare JSON integer:

balance, err := client.Accounts.Balance(ctx)
// balance is *big.Int
fmt.Printf("%s wei\n", balance.String())

// Convert to ETH (1 ETH = 10^18 Wei)
eth := new(big.Float).Quo(
    new(big.Float).SetInt(balance),
    new(big.Float).SetFloat64(1e18),
)
fmt.Printf("%.6f OCTA\n", eth)
FlexInt64

Some fields (e.g. Session.Duration, Session.StartedAt) are returned as bare integers by one endpoint and as quoted strings by another (a known API inconsistency). FlexInt64 deserializes both forms transparently. Access the underlying value with .Int64().

Network speed conversion

MachineRental.NetDownMbits / NetUpMbits and VPNRelay.NetDownMbits / NetUpMbits store raw API values. When the raw value exceeds 100 000, it is in bytes/sec and must be divided by 125 000 to obtain Mbit/s. Use the provided helpers — they apply this logic automatically:

fmt.Printf("Down: %.0f Mbps\n", machine.NetDownMbps())
fmt.Printf("Up:   %.0f Mbps\n", machine.NetUpMbps())

fmt.Printf("Down: %.0f Mbps\n", relay.DownloadMbps())
fmt.Printf("Up:   %.0f Mbps\n", relay.UploadMbps())

Running examples

A complete working example is in examples/basic:

# Public endpoints only
go run ./examples/basic

# All endpoints
OCTA_API_KEY=<your-key> go run ./examples/basic

Smoke tests

cmd/smoke verifies the SDK against the live API and reports pass/fail for every endpoint:

# Public endpoints only
go run ./cmd/smoke

# All endpoints
OCTA_API_KEY=<your-key> go run ./cmd/smoke

# Machine-readable JSON output
OCTA_API_KEY=<your-key> go run ./cmd/smoke -json

# Run a single check
go run ./cmd/smoke -only network.info

# Custom base URL and timeout
go run ./cmd/smoke -base-url https://api.octa.space -timeout 10s

Running tests

Tests use httptest.Server — no API key or network access required.

go test ./...

# With race detector
go test -race ./...

# With coverage
go test -coverprofile=coverage.out ./...
go tool cover -func=coverage.out

Project layout

octaspace/
├── client.go               — NewClient, options, HTTP transport, retry logic
├── errors.go               — typed error hierarchy
├── bigint.go               — BigInt and FlexInt64 types
├── accounts.go             — Accounts resource
├── apps.go                 — Apps resource
├── idle_jobs.go            — IdleJobs resource
├── network.go              — Network resource
├── nodes.go                — Nodes resource
├── services.go             — ServicesResource (aggregates MR, VPN, Render, Session)
├── services_mr.go          — Machine Rental sub-resource
├── services_render.go      — Render sub-resource
├── services_session_proxy.go — per-session operations (Info, Logs, Stop)
├── services_vpn.go         — VPN sub-resource
├── sessions.go             — Sessions resource
├── examples/basic/         — runnable usage example
└── cmd/smoke/              — live API smoke test tool

Changelog

See CHANGELOG.md for a full history of changes.

License

MIT — see LICENSE.

Documentation

Overview

Package octaspace provides a Go client library for the OctaSpace API.

Basic usage:

client := octaspace.NewClient(os.Getenv("OCTA_API_KEY"))
account, err := client.Accounts.Profile(ctx)

The client is organized into resource sub-clients that mirror the API structure:

client.Accounts         – account profile, balance, wallet generation
client.Nodes            – compute node listing and management
client.Sessions         – active session listing
client.Apps             – available application catalog
client.Network          – network information
client.Services.MR      – machine rental (compute)
client.Services.VPN     – VPN service
client.Services.Render  – render service
client.Services.Session – per-session operations (info, logs, stop)
client.IdleJobs         – idle job inspection

All methods accept a context.Context as the first argument and return a typed value and an error. Errors are one of the types defined in errors.go and can be inspected with errors.As or the helper functions IsNotFound, IsRateLimit, and IsAuthError.

Index

Examples

Constants

View Source
const (
	ServiceMR     = "mr"     // machine rental (compute)
	ServiceVPN    = "vpn"    // VPN relay
	ServiceRender = "render" // render job
)

Service kind constants — these are the values returned in [Session.Service] and used as URL path segments for service-specific endpoints.

View Source
const Version = "0.1.0"

Version is the current SDK version, embedded in the default User-Agent header.

Variables

This section is empty.

Functions

func IsAuthError

func IsAuthError(err error) bool

IsAuthError reports whether err (or any error in its chain) is an *AuthenticationError.

func IsNotFound

func IsNotFound(err error) bool

IsNotFound reports whether err (or any error in its chain) is a *NotFoundError.

func IsRateLimit

func IsRateLimit(err error) bool

IsRateLimit reports whether err (or any error in its chain) is a *RateLimitError.

Types

type APIError

type APIError struct {
	StatusCode int    // HTTP status code from the response
	RequestID  string // value of the X-Request-Id response header, if present
	RetryAfter int    // seconds parsed from the Retry-After header; 0 if absent
	Message    string // human-readable message extracted from the response body
}

APIError represents an error response received from the OctaSpace API (HTTP 4xx or 5xx). Callers should prefer errors.As to unwrap a specific subtype such as *NotFoundError rather than comparing StatusCode directly.

Example (ErrorsAs)

ExampleAPIError_errorsAs shows how to inspect typed API errors returned by the SDK. All errors produced by SDK methods can be checked with errors.As.

package main

import (
	"context"
	"errors"
	"fmt"
	"net/http"
	"net/http/httptest"

	octaspace "github.com/octaspace/go-sdk"
)

func main() {
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusUnauthorized)
		w.Write([]byte(`{"error":"invalid api key"}`))
	}))
	defer srv.Close()

	client := octaspace.NewClient("bad-key",
		octaspace.WithBaseURL(srv.URL),
		octaspace.WithMaxRetries(0),
	)

	_, err := client.Accounts.Profile(context.Background())
	if err != nil {
		var apiErr *octaspace.APIError
		switch {
		case errors.As(err, &apiErr):
			fmt.Printf("HTTP %d: %s\n", apiErr.StatusCode, apiErr.Message)
		case octaspace.IsAuthError(err):
			fmt.Println("authentication failed")
		case octaspace.IsNotFound(err):
			fmt.Println("resource not found")
		case octaspace.IsRateLimit(err):
			fmt.Println("rate limited — check Retry-After")
		default:
			fmt.Printf("unexpected error: %v\n", err)
		}
	}
}
Output:
HTTP 401: invalid api key

func (*APIError) Error

func (e *APIError) Error() string

type Account

type Account struct {
	AccountUUID string `json:"account_uuid"`
	Avatar      string `json:"avatar"`
	Balance     BigInt `json:"balance"`
	Email       string `json:"email"`
	RefCode     string `json:"ref_code"`
	UID         int64  `json:"uid"`
	Wallet      string `json:"wallet"`
}

Account holds OctaSpace account information.

type AccountsResource

type AccountsResource struct {
	// contains filtered or unexported fields
}

AccountsResource provides access to account endpoints. Obtain via [Client.Accounts].

func (*AccountsResource) Balance

func (r *AccountsResource) Balance(ctx context.Context) (*big.Int, error)

Balance returns the authenticated account's balance in Wei.

GET /accounts/balance

func (*AccountsResource) GenerateWallet

func (r *AccountsResource) GenerateWallet(ctx context.Context) (*Account, error)

GenerateWallet creates a new wallet for the authenticated account.

POST /accounts

func (*AccountsResource) Profile

func (r *AccountsResource) Profile(ctx context.Context) (*Account, error)

Profile returns the authenticated account's information.

GET /accounts

type App

type App struct {
	UUID        string         `json:"uuid"`
	Name        string         `json:"name"`
	Image       string         `json:"image"`
	Category    string         `json:"category"`
	Description string         `json:"description"`
	Envs        map[string]any `json:"envs"`
	Ports       []int          `json:"ports"`
	HTTPPorts   []int          `json:"http_ports"`
	Personal    bool           `json:"personal"`
	Extra       map[string]any `json:"extra"`
}

App represents an application available for deployment.

func (*App) UnmarshalJSON

func (a *App) UnmarshalJSON(data []byte) error

UnmarshalJSON accepts both the documented array form and the live string-encoded form used by /apps for ports/http_ports.

type AppsResource

type AppsResource struct {
	// contains filtered or unexported fields
}

AppsResource provides access to the application catalog endpoint. Obtain via [Client.Apps].

func (*AppsResource) List

func (r *AppsResource) List(ctx context.Context) ([]App, error)

List returns all available applications.

GET /apps

type AuthenticationError

type AuthenticationError struct{ *APIError }

AuthenticationError is returned for HTTP 401 Unauthorized responses.

func (*AuthenticationError) Unwrap

func (e *AuthenticationError) Unwrap() error

type BigInt

type BigInt struct {
	*big.Int
}

BigInt is a *big.Int that marshals and unmarshals as a JSON number (bare or quoted). It is used for large integer values such as Wei-denominated balances that exceed the safe range of float64.

A JSON null unmarshals to a zero-value BigInt (Int == nil); callers should check this before dereferencing.

func (BigInt) MarshalJSON

func (b BigInt) MarshalJSON() ([]byte, error)

MarshalJSON implements encoding/json.Marshaler. A nil underlying Int marshals to the bare number 0.

func (*BigInt) UnmarshalJSON

func (b *BigInt) UnmarshalJSON(data []byte) error

UnmarshalJSON implements encoding/json.Unmarshaler.

type Client

type Client struct {
	Accounts *AccountsResource
	Nodes    *NodesResource
	Sessions *SessionsResource
	Apps     *AppsResource
	Network  *NetworkResource
	Services *ServicesResource
	IdleJobs *IdleJobsResource
	// contains filtered or unexported fields
}

Client is an authenticated HTTP client for the OctaSpace API. It is safe for concurrent use. Create one with NewClient.

func NewClient

func NewClient(apiKey string, opts ...Option) *Client

NewClient creates a new OctaSpace API client authenticated with apiKey. Pass an empty string to use only unauthenticated public endpoints (e.g. Network.Info).

Example

ExampleNewClient shows the minimal client setup using an API key from the environment.

package main

import (
	"os"

	octaspace "github.com/octaspace/go-sdk"
)

func main() {
	client := octaspace.NewClient(os.Getenv("OCTA_API_KEY"))
	_ = client
}
Example (Options)

ExampleNewClient_options shows all available configuration options.

package main

import (
	"log"
	"os"

	octaspace "github.com/octaspace/go-sdk"
)

func main() {
	l := log.New(os.Stderr, "[octa] ", log.LstdFlags)

	client := octaspace.NewClient(
		os.Getenv("OCTA_API_KEY"),
		octaspace.WithBaseURLs([]string{
			"https://api.octa.space",
			"https://api-backup.octa.space", // failover if primary is unreachable
		}),
		octaspace.WithTimeout(15_000_000_000), // 15s per-request timeout
		octaspace.WithMaxRetries(3),
		octaspace.WithLogger(octaspace.NewStdLogger(l)),
		octaspace.WithUserAgent("my-app/1.0"),
	)
	_ = client
}

type FlexInt64

type FlexInt64 int64

FlexInt64 unmarshals a JSON value that may be a bare number, a quoted string containing a decimal integer, or null. It is used for fields whose JSON type varies between API endpoints (e.g. Session.Duration is returned as 3600 by /sessions and as "3600" by /sessions?recent=true).

A JSON null or empty string unmarshals to zero.

func (FlexInt64) Int64

func (f FlexInt64) Int64() int64

Int64 returns the underlying int64 value.

func (*FlexInt64) UnmarshalJSON

func (f *FlexInt64) UnmarshalJSON(data []byte) error

UnmarshalJSON implements encoding/json.Unmarshaler.

type GPU

type GPU struct {
	Idx                   int     `json:"idx"`
	Model                 string  `json:"model"`
	BusID                 string  `json:"bus_id"`
	MemTotalMB            int     `json:"mem_total_mb"`
	MemFreeMB             int     `json:"mem_free_mb"`
	GPUTemperature        int     `json:"gpu_temperature"`
	GPUUtilization        int     `json:"gpu_utilization"`
	FanSpeed              int     `json:"fan_speed"`
	PcieLinkGen           int     `json:"pcie_link_gen"`
	PcieLinkWidth         int     `json:"pcie_link_width"`
	PowerLimitWatt        float64 `json:"power_limit_watt"`
	PowerLimitDefaultWatt float64 `json:"power_limit_default_watt"`
	PState                string  `json:"pstate"`
}

GPU represents a single GPU device reported by a node. The same shape is used inside [Node.System] and MachineRental.

type IdleJobsResource

type IdleJobsResource struct {
	// contains filtered or unexported fields
}

IdleJobsResource provides access to idle job endpoints. Obtain via [Client.IdleJobs].

func (*IdleJobsResource) Find

func (r *IdleJobsResource) Find(ctx context.Context, nodeID, jobID int64) (map[string]any, error)

Find returns the status of an idle job identified by nodeID and jobID.

GET /idle_jobs/:node_id/:job_id

func (*IdleJobsResource) Logs

func (r *IdleJobsResource) Logs(ctx context.Context, nodeID, jobID int64) ([]byte, error)

Logs returns the log output of an idle job as raw bytes.

GET /idle_jobs/:node_id/:job_id/logs

type ListNodesParams

type ListNodesParams struct {
	Country *string // filter by ISO country code, e.g. "US" or "DE"
	AppUUID *string // filter by compatible application UUID
}

ListNodesParams contains optional filter parameters for NodesResource.List.

Example

ExampleListNodesParams shows how to filter compute nodes by country and compatible application.

package main

import (
	"context"
	"fmt"
	"log"
	"os"

	octaspace "github.com/octaspace/go-sdk"
)

func main() {
	client := octaspace.NewClient(os.Getenv("OCTA_API_KEY"))
	ctx := context.Background()

	country := "DE"
	nodes, err := client.Nodes.List(ctx, &octaspace.ListNodesParams{
		Country: &country,
	})
	if err != nil {
		log.Fatal(err)
	}
	for _, n := range nodes {
		fmt.Printf("node %d  %s  %s\n", n.ID, n.Location.City, n.State)
	}
}

type ListSessionsParams

type ListSessionsParams struct {
	Recent *bool // set to true to include only recent sessions
}

ListSessionsParams contains optional filter parameters for SessionsResource.List.

type Logger

type Logger interface {
	Debugf(format string, v ...any)
	Errorf(format string, v ...any)
}

Logger is the interface for SDK diagnostic logging. It requires Debugf and Errorf methods. The standard library *log.Logger does NOT satisfy this interface directly — use NewStdLogger to wrap it.

Example with the standard library:

l := log.New(os.Stderr, "[octaspace] ", log.LstdFlags)
client := octaspace.NewClient(apiKey, octaspace.WithLogger(octaspace.NewStdLogger(l)))

func NewStdLogger

func NewStdLogger(l interface{ Printf(string, ...any) }) Logger

NewStdLogger wraps a standard library *log.Logger (or any type with a Printf method) into a Logger suitable for WithLogger. Both Debugf and Errorf are mapped to Printf.

Example

ExampleNewStdLogger shows how to attach a standard library logger for request/response diagnostics. Note: *log.Logger does not directly satisfy the Logger interface; use NewStdLogger to wrap it.

package main

import (
	"log"
	"os"

	octaspace "github.com/octaspace/go-sdk"
)

func main() {
	l := log.New(os.Stderr, "[octaspace] ", log.LstdFlags)
	client := octaspace.NewClient(
		os.Getenv("OCTA_API_KEY"),
		octaspace.WithLogger(octaspace.NewStdLogger(l)),
	)
	_ = client
}

type LogsParams

type LogsParams struct {
	// Recent must be set to true to fetch logs for finished (recent) sessions.
	// Active (current) sessions do not require this parameter.
	Recent *bool
}

LogsParams holds optional parameters for fetching session logs.

Example

ExampleLogsParams shows how to fetch logs for a finished (recent) session. Active sessions do not need LogsParams; pass nil instead.

package main

import (
	"context"
	"os"

	octaspace "github.com/octaspace/go-sdk"
)

func main() {
	client := octaspace.NewClient(os.Getenv("OCTA_API_KEY"))
	ctx := context.Background()
	uuid := "550e8400-e29b-41d4-a716-446655440000"

	proxy := client.Services.Session(uuid)

	// Active session — no params needed.
	_, _ = proxy.Logs(ctx, nil)

	// Finished (recent) session — recent=true is required by the API.
	recent := true
	_, _ = proxy.Logs(ctx, &octaspace.LogsParams{Recent: &recent})
}

type MachineRental

type MachineRental struct {
	NodeID       int64  `json:"node_id"`
	Country      string `json:"country"`
	CountryISO   string `json:"country_iso"`
	City         string `json:"city"`
	Arch         string `json:"arch"`
	OSVersion    string `json:"os_version"`
	Virt         string `json:"virt"` // virtualisation type, e.g. "none", "kvm"
	MemoryType   string `json:"memory_type"`
	CPUModelName string `json:"cpu_model_name"`
	CPUVendorID  string `json:"cpu_vendor_id"`
	CPUCores     int    `json:"cpu_cores"`
	CPUSpeed     int    `json:"cpu_speed"`    // MHz
	MemorySpeed  int    `json:"memory_speed"` // MHz
	TotalMemory  int64  `json:"total_memory"` // bytes
	FreeDisk     int64  `json:"free_disk"`    // bytes
	DiskSpeedMBS int    `json:"disk_speed_mbs"`
	CUDAVersion  string `json:"cuda_version"`
	GPUVendor    string `json:"gpu_vendor"`
	IsHasGPU     bool   `json:"is_has_gpu"`
	IsHiveOS     bool   `json:"is_hive_os"`
	IsWSL        bool   `json:"is_wsl"`
	GPUPT        bool   `json:"gpu_pt"` // GPU passthrough
	GDM          bool   `json:"gdm"`

	// Scores
	AIScore      int `json:"ai_score"`
	BlenderScore int `json:"blender_score"`

	// Pricing (USD, values are scaled by 10 000; divide by 10 000 to get $/hr)
	CurrencyUSD   bool    `json:"currency_usd"`
	MarketPrice   float64 `json:"market_price"` // ETH/USD rate (used when CurrencyUSD=false)
	TotalPriceUSD float64 `json:"total_price_usd"`
	BaseUSD       int     `json:"base_usd"`
	StorageUSD    int     `json:"storage_usd"`
	TrafficUSD    int     `json:"traffic_usd"`

	// Pricing in Ether
	TotalPriceEther float64 `json:"total_price_ether"`
	BaseEther       float64 `json:"base_ether"`
	StorageEther    float64 `json:"storage_ether"`
	TrafficEther    float64 `json:"traffic_ether"`

	// Network speed. Raw values >100 000 are in bytes/sec; divide by 125 000
	// to convert to Mbps. Use NetDownMbps() / NetUpMbps() helpers.
	NetDownMbits int64 `json:"net_down_mbits"`
	NetUpMbits   int64 `json:"net_up_mbits"`

	// Reliability stats
	Reliability struct {
		Uptime    float64 `json:"uptime"`
		Stability float64 `json:"stability"`
		Rating    struct {
			Count int     `json:"count"`
			Score float64 `json:"score"`
		} `json:"rating"`
	} `json:"reliability"`

	// GPU list (flat array; same shape as Node.System.GPUs).
	GPUs []GPU `json:"gpus"`

	AvailablePorts int      `json:"available_ports"`
	Features       []string `json:"features"`
}

MachineRental represents an available machine for rent.

func (MachineRental) NetDownMbps

func (m MachineRental) NetDownMbps() float64

NetDownMbps returns the download speed in Mbit/s. The raw API field net_down_mbits is in bytes/sec when > 100 000.

func (MachineRental) NetUpMbps

func (m MachineRental) NetUpMbps() float64

NetUpMbps returns the upload speed in Mbit/s.

type MachineRentalCreateParams

type MachineRentalCreateParams struct {
	NodeID       int64
	DiskSize     int
	Image        string
	App          string            // application UUID; empty string for custom images
	Envs         map[string]string // environment variables injected into the container
	Ports        []int             // TCP ports to expose
	HTTPPorts    []int             // HTTP ports to expose via reverse proxy
	StartCommand string
	Entrypoint   string
}

MachineRentalCreateParams holds the parameters for creating a machine rental session.

Example

ExampleMachineRentalCreateParams shows how to start a machine rental session with a GPU workload using a custom Docker image.

package main

import (
	"context"
	"fmt"
	"log"
	"os"

	octaspace "github.com/octaspace/go-sdk"
)

func main() {
	client := octaspace.NewClient(os.Getenv("OCTA_API_KEY"))

	resp, err := client.Services.MR.Create(context.Background(), &octaspace.MachineRentalCreateParams{
		NodeID:    98765,
		DiskSize:  50,
		Image:     "nvidia/cuda:12.0-base",
		Ports:     []int{22},
		HTTPPorts: []int{8888}, // Jupyter notebook
		Envs: map[string]string{
			"JUPYTER_TOKEN": "secret",
		},
	})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("session UUID: %s\n", resp.UUID)
}

type MachineRentalCreateResponse

type MachineRentalCreateResponse struct {
	UUID string `json:"uuid"`
}

MachineRentalCreateResponse is returned when a machine rental session is created.

type MachineRentalResource

type MachineRentalResource struct {
	// contains filtered or unexported fields
}

MachineRentalResource provides access to machine rental (MR) service endpoints. Obtain via [Client.Services.MR].

func (*MachineRentalResource) Create

Create starts a new machine rental session.

The API expects the payload as a single-item array with id=0; this method handles that encoding automatically so callers do not need to know the wire format.

POST /services/mr

func (*MachineRentalResource) List

List returns available machines for rent. Pass nil or an empty url.Values to list all available machines.

GET /services/mr

type NetworkError

type NetworkError struct {
	Err error
}

NetworkError wraps low-level transport failures such as DNS resolution errors, TLS handshake failures, or connection refused. It is never returned for HTTP 4xx/5xx responses — those produce an APIError or a subtype.

func (*NetworkError) Error

func (e *NetworkError) Error() string

func (*NetworkError) Unwrap

func (e *NetworkError) Unwrap() error

type NetworkResource

type NetworkResource struct {
	// contains filtered or unexported fields
}

NetworkResource provides access to network information endpoints. Obtain via [Client.Network].

func (*NetworkResource) Info

func (r *NetworkResource) Info(ctx context.Context) (map[string]any, error)

Info returns current network statistics and configuration. This endpoint does not require authentication.

GET /network

type Node

type Node struct {
	ID     int64  `json:"id"`
	IP     string `json:"ip"`
	State  string `json:"state"`  // e.g. "idle", "busy", "offline"
	OSN    string `json:"osn"`    // OctaSpace Node agent version, e.g. "0.0.73"
	Uptime int64  `json:"uptime"` // seconds since agent start
	VrfDt  string `json:"vrf_dt"` // timestamp of the last VRF (verifiable random function) verification

	Location struct {
		City      string  `json:"city"`
		Country   string  `json:"country"`
		Latitude  float64 `json:"latitude"`
		Longitude float64 `json:"longitude"`
	} `json:"location"`

	// Prices are node-owner-configured values denominated in Wei.
	// Use base_usd / storage_usd / traffic_usd on MachineRental for USD prices.
	Prices struct {
		Base    int64 `json:"base"`
		Storage int64 `json:"storage"`
		Traffic int64 `json:"traffic"`
	} `json:"prices"`

	System struct {
		Arch           string  `json:"arch"`
		CPUCores       int     `json:"cpu_cores"`
		CPULoadPercent float64 `json:"cpu_load_percent"`
		CPUModelName   string  `json:"cpu_model_name"`
		OSVersion      string  `json:"os_version"`

		Disk struct {
			Free        int64   `json:"free"`
			Size        int64   `json:"size"`
			Used        int64   `json:"used"`
			UsedPercent float64 `json:"used_percent"`
		} `json:"disk"`

		GPUs []GPU `json:"gpus"`

		Memory struct {
			Free int64 `json:"free"`
			Size int64 `json:"size"`
		} `json:"memory"`
	} `json:"system"`
}

Node represents a compute node in the OctaSpace network.

type NodesResource

type NodesResource struct {
	// contains filtered or unexported fields
}

NodesResource provides access to compute node endpoints. Obtain via [Client.Nodes].

func (*NodesResource) DownloadIdent

func (r *NodesResource) DownloadIdent(ctx context.Context, nodeID int64) ([]byte, error)

DownloadIdent returns the node's identity file as raw bytes.

GET /nodes/:id/ident

func (*NodesResource) DownloadLogs

func (r *NodesResource) DownloadLogs(ctx context.Context, nodeID int64) ([]byte, error)

DownloadLogs returns the node's log archive as raw bytes.

GET /nodes/:id/logs

func (*NodesResource) Find

func (r *NodesResource) Find(ctx context.Context, nodeID int64) (*Node, error)

Find returns a single compute node by its numeric ID.

GET /nodes/:id

func (*NodesResource) List

func (r *NodesResource) List(ctx context.Context, params *ListNodesParams) ([]Node, error)

List returns all compute nodes. Pass nil params to list all nodes without filters.

GET /nodes

func (*NodesResource) Reboot

func (r *NodesResource) Reboot(ctx context.Context, nodeID int64) error

Reboot triggers a reboot of the node.

GET /nodes/:id/reboot

func (*NodesResource) UpdatePrices

func (r *NodesResource) UpdatePrices(ctx context.Context, nodeID int64, prices map[string]any) error

UpdatePrices updates the pricing configuration for a node.

prices is forwarded as-is to the API; the exact set of accepted fields is defined by the OctaSpace API and is not enforced by the SDK. Pass nil to send an empty PATCH (no body).

PATCH /nodes/:id/prices

type NotFoundError

type NotFoundError struct{ *APIError }

NotFoundError is returned for HTTP 404 Not Found responses.

func (*NotFoundError) Unwrap

func (e *NotFoundError) Unwrap() error

type Option

type Option func(*config)

Option is a functional option for configuring a Client.

func WithBaseURL

func WithBaseURL(baseURL string) Option

WithBaseURL overrides the API base URL (default: https://api.octa.space). Equivalent to WithBaseURLs([]string{baseURL}).

func WithBaseURLs

func WithBaseURLs(urls []string) Option

WithBaseURLs sets an ordered list of base URLs. The client tries each URL in order and moves to the next only on network-level failures (connection refused, DNS errors, TLS errors). HTTP errors (4xx, 5xx) are returned immediately without failover. Empty or blank entries are ignored. If the list is empty after filtering, the existing configuration is left unchanged.

Example:

client := octaspace.NewClient(apiKey,
    octaspace.WithBaseURLs([]string{
        "https://api.octa.space",
        "https://api-backup.octa.space",
    }),
)
Example

ExampleWithBaseURLs demonstrates multi-URL failover: the client automatically tries the next URL when a network-level error occurs on the current one.

package main

import (
	"os"

	octaspace "github.com/octaspace/go-sdk"
)

func main() {
	client := octaspace.NewClient(
		os.Getenv("OCTA_API_KEY"),
		octaspace.WithBaseURLs([]string{
			"https://api.octa.space",
			"https://api-backup.octa.space",
		}),
	)
	_ = client
}

func WithHTTPClient

func WithHTTPClient(hc *http.Client) Option

WithHTTPClient replaces the default http.Client. The provided client is used as-is and never mutated by the SDK; its Timeout (if any) still applies in addition to the SDK's own context timeout from WithTimeout.

func WithLogger

func WithLogger(l Logger) Option

WithLogger sets a Logger for request/response diagnostics. Requests and responses are logged at Debug level; transport errors at Error level.

func WithMaxRetries

func WithMaxRetries(n int) Option

WithMaxRetries sets the maximum number of retry attempts for transient errors (HTTP 429, 500, 502, 503, 504) on idempotent requests (default: 2). Set to 0 to disable retries entirely.

func WithTimeout

func WithTimeout(d time.Duration) Option

WithTimeout sets a per-request SDK timeout applied via context.WithTimeout (default: 30s). Pass 0 to disable.

The timeout is independent of any http.Client.Timeout set via WithHTTPClient and the two compose — the shorter bound wins. Unlike a client-level Timeout, WithTimeout is applied per logical SDK call, so it bounds the *total* time including retries.

func WithUserAgent

func WithUserAgent(ua string) Option

WithUserAgent overrides the User-Agent header sent with every request.

type PermissionError

type PermissionError struct{ *APIError }

PermissionError is returned for HTTP 403 Forbidden responses.

func (*PermissionError) Unwrap

func (e *PermissionError) Unwrap() error

type RateLimitError

type RateLimitError struct{ *APIError }

RateLimitError is returned for HTTP 429 Too Many Requests responses. Check [APIError.RetryAfter] for the recommended wait duration in seconds.

func (*RateLimitError) Unwrap

func (e *RateLimitError) Unwrap() error

type RenderResource

type RenderResource struct {
	// contains filtered or unexported fields
}

RenderResource provides access to render service endpoints. Obtain via [Client.Services.Render].

func (*RenderResource) Create

func (r *RenderResource) Create(ctx context.Context, attrs map[string]any) (map[string]any, error)

Create starts a new render job. attrs may contain any fields accepted by the render API.

POST /services/render

func (*RenderResource) List

func (r *RenderResource) List(ctx context.Context, params url.Values) ([]map[string]any, error)

List returns available render jobs or configurations. Pass nil or an empty url.Values to list all.

GET /services/render

type SSHEndpoint

type SSHEndpoint struct {
	Host string `json:"host"`
	Port int    `json:"port"`
}

SSHEndpoint describes an SSH connection endpoint attached to a session.

type ServerError

type ServerError struct{ *APIError }

ServerError is returned for HTTP 5xx responses not covered by a more specific subtype (502, 503, 504 are still wrapped as ServerError).

func (*ServerError) Unwrap

func (e *ServerError) Unwrap() error

type ServicesResource

type ServicesResource struct {
	MR     *MachineRentalResource
	VPN    *VPNResource
	Render *RenderResource
	// contains filtered or unexported fields
}

ServicesResource aggregates all service sub-resources and per-session operations. Obtain via [Client.Services].

Example:

// Start a machine rental
resp, err := client.Services.MR.Create(ctx, &octaspace.MachineRentalCreateParams{...})

// Inspect a running session
info, err := client.Services.Session("uuid").Info(ctx)

func (*ServicesResource) Session

func (r *ServicesResource) Session(uuid string) *SessionProxy

Session returns a *SessionProxy for operations on a specific service session. The UUID is URL-encoded automatically.

type Session

type Session struct {
	UUID    string `json:"uuid"`
	Service string `json:"service"`
	AppName string `json:"app_name"`
	NodeID  int64  `json:"node_id"`

	// Runtime state (present in active sessions)
	Progress string `json:"progress"`
	IsReady  bool   `json:"is_ready"`
	PublicIP string `json:"public_ip"`

	// Timing — returned as a bare integer or a quoted string depending on endpoint;
	// FlexInt64 handles both transparently.
	Duration   FlexInt64 `json:"duration"`
	StartedAt  FlexInt64 `json:"started_at"`
	FinishedAt FlexInt64 `json:"finished_at"`

	// Network counters (present in recent sessions, returned as quoted strings)
	RX string `json:"rx"`
	TX string `json:"tx"`

	// Billing
	ChargeAmount      BigInt `json:"charge_amount"`
	TerminationReason int    `json:"termination_reason"`

	SSHDirect SSHEndpoint `json:"ssh_direct"`
	SSHProxy  SSHEndpoint `json:"ssh_proxy"`

	URLs map[string]string `json:"urls"`

	Prices struct {
		BaseUSD     int     `json:"base_usd"`
		CurrencyUSD bool    `json:"currency_usd"`
		MarketPrice float64 `json:"market_price"`
	} `json:"prices"`

	NodeHW struct {
		CPU      string `json:"cpu"`
		CPUCores int    `json:"cpu_cores"`
		GPU      []struct {
			Model      string `json:"model"`
			MemTotalMB int    `json:"mem_total_mb"`
		} `json:"gpu"`
		TotalMemory int64 `json:"total_memory"`
		Disk        int64 `json:"disk"`
	} `json:"node_hw"`
}

Session represents an active or recent service session.

Note: the /sessions and /sessions?recent=true endpoints return overlapping but not identical field sets. Fields absent in a response are zero-valued.

type SessionInfo

type SessionInfo struct {
	// VPNConfig contains the WireGuard/OpenVPN configuration for this session.
	// The JSON field name is "config" (verified against ruby-sdk and python-sdk).
	VPNConfig    string `json:"config"`
	TX           int64  `json:"tx"`
	RX           int64  `json:"rx"`
	ChargeAmount BigInt `json:"charge_amount"`
}

SessionInfo holds detailed information about a running service session.

type SessionLogs

type SessionLogs struct {
	System []struct {
		TS  int64  `json:"ts"`
		Msg string `json:"msg"`
	} `json:"system"`
	Container string `json:"container"`
}

SessionLogs holds system and container log output for a session.

type SessionProxy

type SessionProxy struct {
	// contains filtered or unexported fields
}

SessionProxy provides operations on a specific service session. Obtain one via ServicesResource.Session.

Example:

proxy := client.Services.Session("550e8400-e29b-41d4-a716-446655440000")
info, err := proxy.Info(ctx)

func (*SessionProxy) Info

func (s *SessionProxy) Info(ctx context.Context) (*SessionInfo, error)

Info returns detailed information about the session including VPN configuration and traffic statistics.

GET /services/:uuid/info

func (*SessionProxy) Logs

func (s *SessionProxy) Logs(ctx context.Context, params *LogsParams) (*SessionLogs, error)

Logs returns system and container log output for the session. Pass nil params for active (current) sessions. For finished (recent) sessions, set recent := true and pass &LogsParams{Recent: &recent} — the API requires the recent=true query parameter to return logs for sessions that are no longer running.

GET /services/:uuid/logs GET /services/:uuid/logs?recent=true

func (*SessionProxy) Stop

func (s *SessionProxy) Stop(ctx context.Context, params *StopParams) error

Stop terminates the session. Pass nil params to stop without sending a body. Pass a non-nil *StopParams to include optional fields such as a quality score.

POST /services/:uuid/stop

type SessionsResource

type SessionsResource struct {
	// contains filtered or unexported fields
}

SessionsResource provides access to the sessions listing endpoint. Obtain via [Client.Sessions].

func (*SessionsResource) List

func (r *SessionsResource) List(ctx context.Context, params *ListSessionsParams) ([]Session, error)

List returns active and recent sessions. Pass nil params to list all sessions.

GET /sessions

type StopParams

type StopParams struct {
	Score *int `json:"score,omitempty"` // optional quality score (e.g. 1–5)
}

StopParams holds optional parameters for stopping a session.

type VPNCreateParams

type VPNCreateParams struct {
	NodeID         int64          `json:"node_id"`
	SubKind        string         `json:"subkind,omitempty"` // "wg", "openvpn", "ss", "v2ray"
	Protocol       string         `json:"protocol,omitempty"`
	OrganizationID string         `json:"organization_id,omitempty"`
	ProjectID      string         `json:"project_id,omitempty"`
	Extra          map[string]any `json:"-"`
}

VPNCreateParams holds parameters for creating a VPN session.

Protocol is required when SubKind == "v2ray" (e.g. "vmess", "vless"); it is ignored by the API for other subkinds. Values in Extra are merged into the request body and take precedence over the typed fields on key conflict, allowing callers to pass forward-compatible fields without an SDK bump.

Example

ExampleVPNCreateParams shows how to start a WireGuard VPN session and a v2ray session, which requires the Protocol field.

package main

import (
	"context"
	"os"

	octaspace "github.com/octaspace/go-sdk"
)

func main() {
	client := octaspace.NewClient(os.Getenv("OCTA_API_KEY"))
	ctx := context.Background()

	// WireGuard — only NodeID and SubKind are needed.
	_, _ = client.Services.VPN.Create(ctx, &octaspace.VPNCreateParams{
		NodeID:  12345,
		SubKind: "wg",
	})

	// v2ray — Protocol is required.
	_, _ = client.Services.VPN.Create(ctx, &octaspace.VPNCreateParams{
		NodeID:   12345,
		SubKind:  "v2ray",
		Protocol: "vmess",
	})

	// Forward arbitrary fields the API may accept via Extra.
	_, _ = client.Services.VPN.Create(ctx, &octaspace.VPNCreateParams{
		NodeID:  12345,
		SubKind: "wg",
		Extra:   map[string]any{"custom_field": "value"},
	})
}

func (VPNCreateParams) MarshalJSON

func (p VPNCreateParams) MarshalJSON() ([]byte, error)

MarshalJSON serializes VPNCreateParams, merging Extra into the top-level object so callers can forward arbitrary API fields.

type VPNCreateResponse

type VPNCreateResponse struct {
	UUID string `json:"uuid"`
}

VPNCreateResponse is returned when a VPN session is created.

type VPNRelay

type VPNRelay struct {
	NodeID      int64   `json:"node_id"`
	Country     string  `json:"country"`
	CountryISO  string  `json:"country_iso"`
	City        string  `json:"city"`
	Latitude    float64 `json:"latitude"`
	Longitude   float64 `json:"longitude"`
	Residential bool    `json:"residential"`

	// Pricing
	PricePerGB      float64 `json:"traffic_price_usd"`
	PricePerGBEther float64 `json:"traffic_price_ether"`

	// Network speed. Raw values >100 000 are in bytes/sec; divide by 125 000
	// to convert to Mbps. Use DownloadMbps() / UploadMbps() helpers.
	NetDownMbits int64 `json:"net_down_mbits"`
	NetUpMbits   int64 `json:"net_up_mbits"`
}

VPNRelay represents an available VPN relay node.

func (VPNRelay) DownloadMbps

func (r VPNRelay) DownloadMbps() float64

DownloadMbps returns the download speed in Mbit/s.

func (VPNRelay) UploadMbps

func (r VPNRelay) UploadMbps() float64

UploadMbps returns the upload speed in Mbit/s.

type VPNResource

type VPNResource struct {
	// contains filtered or unexported fields
}

VPNResource provides access to VPN service endpoints. Obtain via [Client.Services.VPN].

func (*VPNResource) Create

func (r *VPNResource) Create(ctx context.Context, params *VPNCreateParams) (*VPNCreateResponse, error)

Create starts a new VPN session on the specified relay node.

POST /services/vpn

func (*VPNResource) List

func (r *VPNResource) List(ctx context.Context, params url.Values) ([]VPNRelay, error)

List returns available VPN relay nodes. Pass nil or an empty url.Values to list all relays.

GET /services/vpn

type ValidationError

type ValidationError struct{ *APIError }

ValidationError is returned for HTTP 422 Unprocessable Entity responses.

func (*ValidationError) Unwrap

func (e *ValidationError) Unwrap() error

Directories

Path Synopsis
cmd
smoke command
smoke is a CLI tool for verifying the OctaSpace Go SDK against a live API.
smoke is a CLI tool for verifying the OctaSpace Go SDK against a live API.
examples
basic command
Basic usage example for the OctaSpace Go SDK.
Basic usage example for the OctaSpace Go SDK.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL