🚀 Join the Community! Connect with security researchers, AI enthusiasts, and fellow ethical hackers. Get support, share insights, and stay updated with the latest PentAGI developments.
The VXControl Cloud SDK enables developers to integrate their security tools and applications with the VXControl Cloud Intelligence Platform, providing access to advanced cybersecurity services including threat intelligence, vulnerability databases, computational resources, AI-powered troubleshooting, and automated update systems.
- Type-Safe API: 24 strongly-typed function patterns covering all request/response scenarios
- Transparent Security: Automatic proof-of-work solving and end-to-end encryption
- Performance Optimized: HTTP/2 support, connection pooling, streaming encryption
- Enterprise Ready: Comprehensive error handling, retry logic, and production monitoring
- License Integration: Built-in premium feature validation and tier management
- Endpoint Health Probing:
Check()API for pre-flight connectivity and quota verification - Structured Rate Limit Errors:
RateLimitError/QuotaErrorcarry server-advertisedRetry-Aftercooldowns - Context-Safe Retries: Cancelled context during back-off preserves the last
*RateLimitErrorso callers can still readRetryAfter
go get github.com/vxcontrol/cloud/sdkpackage main
import (
"context"
"encoding/json"
"log"
"github.com/vxcontrol/cloud/models"
"github.com/vxcontrol/cloud/sdk"
"github.com/vxcontrol/cloud/system"
"github.com/sirupsen/logrus"
)
type Client struct {
UpdatesCheck sdk.CallReqBytesRespBytes
ReportError sdk.CallReqBytesRespBytes
}
func main() {
var client Client
// Configure endpoints
configs := []sdk.CallConfig{
{
Calls: []any{&client.UpdatesCheck},
Host: "update.pentagi.com",
Name: "updates_check",
Path: "/api/v1/proxy/updates/check",
Method: sdk.CallMethodPOST,
},
{
Calls: []any{&client.ReportError},
Host: "support.pentagi.com",
Name: "errors_report",
Path: "/api/v1/proxy/errors/report",
Method: sdk.CallMethodPOST,
},
}
// Initialize SDK
err := sdk.Build(configs,
sdk.WithClient("MySecTool", "1.0.0"),
sdk.WithInstallationID(system.GetInstallationID()),
sdk.WithLogger(sdk.WrapLogrus(logrus.StandardLogger())),
sdk.WithLicenseKey("XXXX-XXXX-XXXX-XXXX"),
)
if err != nil {
log.Fatal("SDK initialization failed:", err)
}
// Check for updates. Strategy is required — it tells the server whether to
// resolve components against a curated release ("stable"), a release plus
// channel metadata ("preview"), or the raw channel alone ("nightly").
updateReq := models.CheckUpdatesRequest{
InstallerVersion: "1.0.0",
InstallerOS: models.OSTypeLinux,
InstallerArch: models.ArchTypeAMD64,
Strategy: models.UpdateStrategyStable,
}
data, _ := json.Marshal(updateReq)
response, err := client.UpdatesCheck(context.Background(), data)
if err != nil {
log.Fatal("Update check failed:", err)
}
// Every response body arrives wrapped in {"status":…,"data":…}. Decoding it
// straight into CheckUpdatesResponse "succeeds" and silently yields an empty
// update list — always unwrap it with ParseEnvelope instead.
updateResp, err := models.ParseEnvelope[models.CheckUpdatesResponse](response)
if err != nil {
log.Fatal("Update check failed:", err)
}
log.Printf("Available updates: %+v", updateResp.Updates)
}graph TD
A[Your Security Application] --> B[VXControl Cloud SDK]
B --> C[PoW Challenge System]
B --> D[Encrypted Transport Layer]
D --> E[VXControl Cloud Platform]
E --> F[Update Services]
E --> G[Package Management]
E --> H[Error Reporting & AI Support]
E --> I[Threat Intelligence Hub]
E --> J[Vulnerability Database]
E --> K[Computational Resources]
E --> L[Knowledge Base]
A --> M[PentAGI]
A --> N[Security Tools]
A --> O[SOC Systems]
A --> P[Red Team Tools]
A --> Q[Custom Applications]
C --> R[Memory-Hard Algorithm]
C --> S[Rate Limiting]
D --> T[End-to-End Encryption]
D --> V[Forward Secrecy]
D --> X[AES-CBC Request Signing]
Keep PentAGI with automated update checking:
import "github.com/vxcontrol/cloud/models"
// Check for component updates. Installed artefacts are reported separately by
// how they are delivered: pulled container images vs. downloaded files.
// Report every product stack you know about too, including unused ones — a
// stack hosted externally and a stack nobody uses look identical otherwise.
updateReq := models.CheckUpdatesRequest{
InstallerVersion: "1.0.0",
InstallerOS: models.OSTypeLinux,
InstallerArch: models.ArchTypeAMD64,
Strategy: models.UpdateStrategyStable,
Images: []models.ImageComponentInfo{
{
Component: models.ComponentTypePentagi,
Status: models.ComponentStatusRunning,
OS: models.OSTypeLinux, // the ARTEFACT's platform, not the host's
Arch: models.ArchTypeAMD64,
Repository: "vxcontrol/pentagi",
Tag: "latest",
},
},
Stacks: []models.StackInfo{
{Stack: models.ProductStackPentagi, Status: models.StackStatusInstalled},
},
}
data, _ := json.Marshal(updateReq)
response, err := client.UpdatesCheck(ctx, data)
updateResp, err := models.ParseEnvelope[models.CheckUpdatesResponse](response)Get intelligent assistance for troubleshooting:
// Report an error for analysis
errorReq := models.SupportErrorRequest{
Component: models.ComponentTypePentagi,
Version: "1.0.0",
OS: models.OSTypeLinux,
Arch: models.ArchTypeAMD64,
ErrorDetails: map[string]any{
"error_type": "connection_timeout",
"message": "Failed to connect to target",
"context": map[string]string{"target": "192.168.1.1", "port": "443"},
},
}
data, _ := json.Marshal(errorReq)
response, err := client.ReportError(ctx, data)Download and validate software packages:
// Get package information
packageReq := models.PackageInfoRequest{
Component: models.ComponentTypePentagi,
Version: "1.0.0",
OS: models.OSTypeLinux,
Arch: models.ArchTypeAMD64,
}
// Validate package integrity with signatures
signature := models.SignatureValue("base64-encoded-signature")
fileData, _ := os.ReadFile("package.tar.gz")
if err := signature.ValidateData(fileData); err != nil {
log.Fatal("Package signature validation failed:", err)
}Interactive support with investigation capabilities:
// Create support issue
issueReq := models.SupportIssueRequest{
Component: models.ComponentTypeEngine,
Version: "2.0.0",
OS: models.OSTypeDarwin,
Arch: models.ArchTypeARM64,
ErrorDetails: "Scanner fails to detect specific vulnerability patterns",
Logs: []models.SupportLogs{
{
Component: models.ComponentTypeEngine,
Logs: []string{"ERROR: Pattern matching timeout", "WARN: Memory usage high"},
},
},
}
// Investigate with AI assistance. IssueID is the value returned by the
// SupportIssueResponse above — it is how a client keeps a multi-turn
// conversation about the same issue across separate calls.
investigationReq := models.SupportInvestigationRequest{
IssueID: receivedIssueID,
UserInput: "The scanner works fine with other patterns but fails on this specific CVE",
}
// Bind IssueInvestigate to CallReqBytesRespBytes for the default JSON answer:
data, _ := json.Marshal(investigationReq)
response, err := client.IssueInvestigate(ctx, data)
answer, err := models.ParseEnvelope[models.SupportInvestigationResponse](response)
// answer.Answer is the reply text; answer.MsgLogs carries the full anonymised
// conversation transcript when the server attaches one.
// Or set UseStream and bind IssueInvestigate to CallReqBytesRespReader instead,
// to receive the same answer incrementally as Server-Sent Events — the stream
// is NOT wrapped in the {"status":…,"data":…} envelope, unlike every other response.
investigationReq.UseStream = trueThe SDK generates 24 strongly-typed function patterns, one for every combination of request shape (none / query / path args / query+args), request body (none / bytes / reader), and response shape (bytes / reader / writer). Assign the field a value of one of these types and sdk.Build fills it in for the CallConfig it matches:
| Pattern | Request | Body | Response | Use Case |
|---|---|---|---|---|
CallReqRespBytes |
None | None | Bytes | Simple JSON/binary retrieval |
CallReqRespReader |
None | None | Reader | Large downloads as a stream |
CallReqRespWriter |
None | None | Writer | Stream a response into an io.Writer |
CallReqQueryRespBytes |
Query | None | Bytes | Filtered queries (?limit=10&offset=20) |
CallReqQueryRespReader |
Query | None | Reader | Query-based downloads |
CallReqQueryRespWriter |
Query | None | Writer | Query-based streaming (used by download-installer) |
CallReqWithArgsRespBytes |
Path args | None | Bytes | RESTful resource access (/users/:id) |
CallReqWithArgsRespReader |
Path args | None | Reader | Resource-specific downloads |
CallReqWithArgsRespWriter |
Path args | None | Writer | Resource-specific streaming |
CallReqQueryWithArgsRespBytes |
Path args + Query | None | Bytes | Combined path + query lookups |
CallReqQueryWithArgsRespReader |
Path args + Query | None | Reader | Combined lookups, streamed response |
CallReqQueryWithArgsRespWriter |
Path args + Query | None | Writer | Combined lookups streamed to a writer |
CallReqBytesRespBytes |
None | Bytes | Bytes | JSON API calls (used by check-update, report-errors) |
CallReqBytesRespReader |
None | Bytes | Reader | JSON request, SSE/streamed response (AI investigation) |
CallReqBytesRespWriter |
None | Bytes | Writer | JSON request, response streamed to a writer |
CallReqReaderRespBytes |
None | Reader | Bytes | Streamed upload, JSON response |
CallReqReaderRespReader |
None | Reader | Reader | Stream-to-stream processing |
CallReqReaderRespWriter |
None | Reader | Writer | Streamed upload with streamed output |
CallReqBytesWithArgsRespBytes |
Path args | Bytes | Bytes | Resource updates with a JSON body |
CallReqBytesWithArgsRespReader |
Path args | Bytes | Reader | Resource updates with a streamed response |
CallReqBytesWithArgsRespWriter |
Path args | Bytes | Writer | Resource updates streamed to a writer |
CallReqReaderWithArgsRespBytes |
Path args | Reader | Bytes | Streamed uploads to a specific resource |
CallReqReaderWithArgsRespReader |
Path args | Reader | Reader | Resource-targeted stream processing |
CallReqReaderWithArgsRespWriter |
Path args | Reader | Writer | Resource-targeted streamed upload/download |
Note: only calls with a
[]bytebody (*Bytes*variants) are retried automatically on a temporary failure — see Automatic Retry Logic. A call built with a rawio.Readerbody cannot be rewound after a failed attempt, so the SDK disables retries for it.
err := sdk.Build(configs,
// Required: Client identification
sdk.WithClient("MyApp", "1.0.0"),
// Optional: Premium features
sdk.WithLicenseKey("XXXX-XXXX-XXXX-XXXX"),
// Optional: Performance tuning
sdk.WithPowTimeout(30*time.Second),
sdk.WithMaxRetries(3),
)// Custom transport for proxies/certificates
transport := sdk.DefaultTransport()
transport.TLSClientConfig = &tls.Config{
MinVersion: tls.VersionTLS12,
// custom certificate validation
}
transport.Proxy = http.ProxyURL(proxyURL)
// Custom structured logging
logger := logrus.New()
logger.SetLevel(logrus.InfoLevel)
err := sdk.Build(configs,
sdk.WithTransport(transport),
sdk.WithLogger(sdk.WrapLogrus(logger)),
sdk.WithInstallationID(system.GetInstallationID()),
)All API calls require solving computational challenges to prevent abuse and DDoS attacks. The SDK automatically:
- Requests challenge tickets from the server
- Solves memory-hard proof-of-work puzzles
- Includes cryptographic signatures with requests
- Session Keys: Ephemeral AES keys for each request
- NaCL Encryption: Secure key exchange using Curve25519
- Streaming Cipher: AES-GCM for large data transfers
- Forward Secrecy: Cypher key rotation
- Adaptive Difficulty: PoW complexity scales with server load
- Tier-Based Access: License validation determines API quotas
- Intelligent Retry: Automatic backoff with server-provided timing
The SDK defines three layers of errors:
- Sentinel errors — comparable with
errors.Is, e.g.sdk.ErrTooManyRequestsRPM - Wrapper types — carry extra fields, extractable with
errors.As:*sdk.RateLimitError— wraps RPM/RPH/RPD/general rate-limit sentinels and carries the server-advertisedRetry-Aftercooldown*sdk.QuotaError— wraps license-tier quota sentinels (Blocked,Daily,Monthly) and carries theRetry-Afterreset cooldown
- Joined context errors — when a context is cancelled during back-off, the SDK returns
fmt.Errorf("%w: %w", ctx.Err(), lastRateLimitErr), preserving both the context error and the rate-limit wrapper
Use sdk.RetryAfterOf(err) to extract the server-suggested retry delay from any error, without needing to type-assert to *RateLimitError or *QuotaError directly:
response, err := client.UpdatesCheck(ctx, data)
if err != nil {
if wait := sdk.RetryAfterOf(err); wait > 0 {
log.Printf("server asks to retry after %s", wait)
time.Sleep(wait)
}
}response, err := client.QueryThreats(ctx, body)
if err != nil {
// Fine-grained rate-limit classification
var rle *sdk.RateLimitError
if errors.As(err, &rle) {
switch rle.Scope {
case sdk.RateLimitScopeRPM:
// minute-window: SDK already retries automatically up to maxRetries
time.Sleep(rle.RetryAfter)
case sdk.RateLimitScopeRPH:
// hour-window: fatal, do not auto-retry
log.Printf("hourly limit reached, retry after %s", rle.RetryAfter)
case sdk.RateLimitScopeRPD:
// day-window: fatal, do not auto-retry
log.Printf("daily limit reached, retry after %s", rle.RetryAfter)
}
return
}
// Quota / license-tier errors
var qe *sdk.QuotaError
if errors.As(err, &qe) {
switch qe.Scope {
case sdk.QuotaScopeBlocked:
log.Println("endpoint not available for this license tier")
case sdk.QuotaScopeDaily:
log.Printf("daily quota exhausted, reset in %s", qe.RetryAfter)
case sdk.QuotaScopeMonthly:
log.Printf("monthly quota exhausted, reset in %s", qe.RetryAfter)
}
return
}
}// Temporary errors (automatically retried up to WithMaxRetries):
// - Server overload (sdk.ErrBadGateway, sdk.ErrServerInternal) → 3s backoff
// - General rate limits (sdk.ErrTooManyRequests) → 5s backoff
// - RPM rate limits (sdk.ErrTooManyRequestsRPM) → Retry-After header (capped at DefaultWaitTime=10s)
// - PoW timeouts (sdk.ErrExperimentTimeout) → DefaultWaitTime=10s backoff
// Fatal errors (no retry):
// - Invalid requests (sdk.ErrBadRequest, sdk.ErrForbidden, sdk.ErrNotFound)
// - Long-term rate limits (sdk.ErrTooManyRequestsRPH, sdk.ErrTooManyRequestsRPD)
// - Quota errors (sdk.ErrQuotaBlocked, sdk.ErrQuotaExceededDaily, sdk.ErrQuotaExceededMonthly)The calculateWaitTime logic now prefers the server-advertised Retry-After value from *RateLimitError (capped at DefaultWaitTime) over fixed fallback delays.
Streamed request bodies are never retried. Calls built with a raw
io.Readerbody (CallReqReaderRespBytesand its siblings) cannot be rewound after a failed attempt, so the SDK forces a single attempt for them regardless ofWithMaxRetries. Calls with a[]bytebody (CallReqBytesRespBytesand its siblings — what all three examples use) are retried normally: a fresh reader is created for every attempt.
data, err := api.QueryThreats(ctx, []byte(threatQuery))
if err != nil {
// Check for server-suggested retry delay first (works for both RateLimitError and QuotaError)
if wait := sdk.RetryAfterOf(err); wait > 0 {
log.Printf("server suggests waiting %s before retry", wait)
}
switch {
case errors.Is(err, sdk.ErrTooManyRequestsRPM):
// SDK already retried automatically; wait for server-advertised window
time.Sleep(sdk.RetryAfterOf(err))
case errors.Is(err, sdk.ErrQuotaBlocked):
// Endpoint not available for current license tier, upgrade required
log.Error("access denied — upgrade license tier")
case errors.Is(err, sdk.ErrForbidden):
// Check license validity or authentication
log.Error("access denied - verify license key")
case errors.Is(err, sdk.ErrExperimentTimeout):
// Increase PoW timeout for slower systems
// Reconfigure with sdk.WithPowTimeout(60*time.Second)
default:
log.Error("unexpected error:", err)
}
}The Check() function probes each configured endpoint by acquiring and solving a PoW ticket without making an actual API call. Use it at startup or in health-check routines to verify reachability and inspect allowed RPM quotas.
configs := []sdk.CallConfig{
{Host: "update.pentagi.com", Name: "updates_check", Path: "/api/v1/proxy/updates/check", Method: sdk.CallMethodPOST},
{Host: "support.pentagi.com", Name: "errors_report", Path: "/api/v1/proxy/errors/report", Method: sdk.CallMethodPOST},
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
statuses, err := sdk.Check(ctx, configs,
sdk.WithClient("MyApp", "1.0.0"),
sdk.WithLicenseKey("XXXX-XXXX-XXXX-XXXX"),
)
if err != nil {
log.Fatal("SDK setup failed:", err)
}
for name, s := range statuses {
if s.IsReachable() {
log.Printf("[%s] reachable, allowed RPM: %d", name, s.AllowedRPM())
} else {
log.Printf("[%s] unreachable: %v", name, s.LastError())
}
}Check() returns sdk.EndpointStatuses — a map[string]EndpointStatus keyed by endpoint Name. Each value exposes:
| Method | Description |
|---|---|
LastError() error |
Last probe error, or nil on success |
AllowedRPM() int |
Server-advertised requests-per-minute quota (0 when unreachable) |
IsReachable() bool |
true when last probe succeeded and AllowedRPM > 0 |
Recheck(ctx) error |
Re-probes the endpoint in place and updates all fields atomically |
// Re-probe a specific endpoint later (e.g. after a rate-limit cooldown)
if err := statuses["updates_check"].Recheck(ctx); err != nil {
log.Println("still unreachable:", err)
} else {
log.Println("now reachable, RPM:", statuses["updates_check"].AllowedRPM())
}Top-level errors from Check() indicate SDK setup failures (crypto, invalid options). Per-endpoint failures are stored inside each EndpointStatus and use the same error sentinel hierarchy as regular calls:
s := statuses["errors_report"]
switch {
case s.LastError() == nil:
// reachable
case errors.Is(s.LastError(), sdk.ErrInvalidConfiguration):
log.Println("bad config — fix CallConfig")
case errors.Is(s.LastError(), sdk.ErrQuotaBlocked):
log.Println("endpoint not available for this license tier")
case errors.Is(s.LastError(), sdk.ErrForbidden):
log.Println("license key rejected by server")
default:
log.Println("network/server error:", s.LastError())
}- License validation: ~334,000 operations/sec
- Function generation: ~2M path templates/sec
- Streaming encryption: ~50MB/sec throughput
- Connection pooling: 300 connections/host, 50 total idle
- Per request: ~300 bytes (context + headers + keys)
- Per SDK instance: ~200KB (connection pools + crypto keys)
- PoW solving: 20-1024KB (reused across attempts)
// Reuse SDK instances across requests
err := sdk.Build(configs, options...)
// Use streaming for large data
reader, err := api.ProcessLargeDataset(ctx, dataStream, dataSize)
// Configure connection pooling for high throughput
transport := sdk.DefaultTransport()
transport.MaxConnsPerHost = 500
sdk.WithTransport(transport)// Minimum production setup
err := sdk.Build(configs,
sdk.WithClient("YourApp", version), // Required: Identification
sdk.WithLicenseKey(licenseKey), // Optional: Authentication
sdk.WithLogger(productionLogger), // Recommended: Monitoring
)// Custom logger for metrics collection
type MetricsLogger struct {
*logrus.Logger
metrics MetricsCollector
}
func (m *MetricsLogger) WithError(err error) sdk.Entry {
// Track error rates by type
m.metrics.IncrementErrorCounter(err)
return m.Logger.WithError(err)
}
// Integration
logger := &MetricsLogger{Logger: logrus.New(), metrics: yourMetrics}
sdk.WithLogger(logger)- Certificate Validating: Validate server certificates in production
- Proxy Support: Configure corporate proxy settings if required
- Timeout Tuning: Adjust PoW timeouts based on hardware capabilities
- Rate Limit Monitoring: Track API quota usage and plan capacity
// Integrate update checking into security tools
func checkSecurityToolUpdates(
images []models.ImageComponentInfo, files []models.FileComponentInfo,
) error {
updateReq := models.CheckUpdatesRequest{
InstallerVersion: getCurrentVersion(),
InstallerOS: getCurrentOS(),
InstallerArch: getCurrentArch(),
Strategy: models.UpdateStrategyStable,
Images: images,
Files: files,
}
data, _ := json.Marshal(updateReq)
response, err := client.UpdatesCheck(context.Background(), data)
if err != nil {
return err
}
updateResp, err := models.ParseEnvelope[models.CheckUpdatesResponse](response)
if err != nil {
return err
}
for _, update := range updateResp.Updates {
if !update.HasUpdate {
continue
}
// CurrentVersion/LatestVersion are only set when the server could
// attribute the stack to a release — read Resolution to see why when
// they are nil (e.g. an installation ahead of any curated release).
log.Printf("Update available for stack %s (resolved via %s)", update.Stack, update.Resolution)
}
return nil
}// Integrate error reporting into application error handling
func reportSecurityToolError(component models.ComponentType, err error) error {
errorReq := models.SupportErrorRequest{
Component: component,
Version: getComponentVersion(component),
OS: getCurrentOS(),
Arch: getCurrentArch(),
ErrorDetails: map[string]any{
"error_message": err.Error(),
"stack_trace": getStackTrace(),
"context": getCurrentContext(),
},
}
data, _ := json.Marshal(errorReq)
_, reportErr := client.ReportError(context.Background(), data)
return reportErr
}// Validate downloaded packages before installation
func validatePackageIntegrity(packagePath, signatureStr string) error {
signature := models.SignatureValue(signatureStr)
// Validate file signature
if err := signature.ValidateFile(packagePath); err != nil {
return fmt.Errorf("package signature validation failed: %w", err)
}
log.Println("Package integrity verified successfully")
return nil
}
// Validate data integrity in memory
func validateDataIntegrity(data []byte, signatureStr string) error {
signature := models.SignatureValue(signatureStr)
if err := signature.ValidateData(data); err != nil {
return fmt.Errorf("data signature validation failed: %w", err)
}
return nil
}// Connect to different service clusters
type FullClient struct {
UpdatesCheck sdk.CallReqBytesRespBytes
ErrorReport sdk.CallReqBytesRespBytes
PackageInfo sdk.CallReqBytesRespBytes
SupportIssue sdk.CallReqBytesRespBytes
}
configs := []sdk.CallConfig{
{
Calls: []any{&client.UpdatesCheck},
Host: "update.pentagi.com",
Name: "updates_check",
Path: "/api/v1/proxy/updates/check",
Method: sdk.CallMethodPOST,
},
{
Calls: []any{&client.ErrorReport},
Host: "support.pentagi.com",
Name: "errors_report",
Path: "/api/v1/proxy/errors/report",
Method: sdk.CallMethodPOST,
},
{
Calls: []any{&client.PackageInfo},
Host: "update.pentagi.com",
Name: "packages_info",
Path: "/api/v1/proxy/packages/info",
Method: sdk.CallMethodPOST,
},
}// Configure timeouts for different operation types
err := sdk.Build(configs,
sdk.WithPowTimeout(30*time.Second), // For slower systems (max 60s, default 10s)
sdk.WithMaxRetries(5), // For rate limiting and network issues
sdk.WithTransport(customTransport), // Custom HTTP configuration
)
// Per-request timeouts
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel()
response, err := client.UpdatesCheck(ctx, requestData)| Error | Type | Retry | Description |
|---|---|---|---|
sdk.ErrBadGateway |
Temporary | Yes (3s) | Server maintenance/overload |
sdk.ErrServerInternal |
Temporary | Yes (3s) | Internal server error |
sdk.ErrTooManyRequests |
Temporary | Yes (5s) | General rate limit exceeded |
sdk.ErrTooManyRequestsRPM |
Temporary | Yes (Retry-After, max 10s) | Per-minute rate limit exceeded |
sdk.ErrExperimentTimeout |
Temporary | Yes (10s) | PoW solving timeout |
sdk.ErrTooManyRequestsRPH |
Fatal | No | Per-hour rate limit exceeded |
sdk.ErrTooManyRequestsRPD |
Fatal | No | Per-day rate limit exceeded |
sdk.ErrForbidden |
Fatal | No | Invalid license or authentication |
sdk.ErrBadRequest |
Fatal | No | Invalid request format |
sdk.ErrNotFound |
Fatal | No | Unknown endpoint or resource |
sdk.ErrQuotaBlocked |
Fatal | Never | Endpoint not available for this license tier |
sdk.ErrQuotaExceededDaily |
Fatal | No (Retry-After via *QuotaError) |
Daily quota exhausted |
sdk.ErrQuotaExceededMonthly |
Fatal | No (Retry-After via *QuotaError) |
Monthly quota exhausted |
Tip: Use
sdk.RetryAfterOf(err)to extract the server-suggested cooldown from any error, regardless of whether it is a*RateLimitErroror*QuotaErroror wrapped further in a context error.
The SDK provides strongly-typed models for all API interactions:
models.ParseEnvelope[T](body): use this to read every JSON response. The server wraps every answer as{"status":…,"data":…}; unmarshalling the body straight into its payload type "succeeds" and silently yields a zero value (e.g. an empty update list), which is the single easiest mistake to make against this API.ParseEnvelopeunwraps it and returns a typedT. A streamed answer (UseStream: trueon an investigation) is the one exception — it is raw SSE, not wrapped in this envelope.*models.APIError: returned byParseEnvelopewhen the envelope'sstatusis not"success"; carriesCode/Msg/Causeas reported by the server.
ComponentType: the component vocabulary. Use the constants —models.ComponentTypePentagiand friends — and readmodels/types.gofor the full set; it is the authority and it grows. A raw string that is not in it fails validation for the WHOLE request, not just for the component carrying it.ComponentType.ArtifactKind()/.IsFileComponent()/.IsImageComponent(): how a component is delivered — pulled image vs. downloaded file — which decides whether it belongs in a request'sImagesorFileslist. Seemodels/artefacts.go.ComponentType.GetProductStack()(andmodels.ComponentToStackMapping): which product stack a component is versioned with — the update check answers per stack, not per component.
ComponentStatus: unused, connected, installed, runningProductStack: pentagi, langfuse, observability, worker, installer, engine, graphiti, browserOSType: windows, linux, darwinArchType: amd64, arm64
CheckUpdatesRequest/CheckUpdatesResponse: Check for component updates.Strategy(UpdateStrategyNightly/Preview/Stable) is required and selects how the server resolves every reported component.ImageComponentInfo/FileComponentInfo: Installed artefacts, reported separately by how they are delivered — an image is named by a registry reference and a digest, a file by a version and a hash, and no artefact is ever both.StackInfo: How this installation uses one product stack (StackStatusUnused/Connected/Installed/External) — report every stack you know about, including unused ones, so "hosted externally" and "not used at all" don't look identical.UpdateInfo: The per-stack answer —Images/Files(each with aComponentActiontelling you what to do about it), plus theReleasescrossed on the way to the target version.ImageUpdate/FileUpdate: One resolved artefact — the reference/package to pull or download, its digests or hash, and whether it is pinned to a curated release.ReleaseNote: One curated release crossed by an update, with its own changelog and release notes.ComponentAction/ComponentReason/StackResolution/StackStatus: The shared vocabulary describing what to do with an artefact and why — seemodels/answer.go.
PackageInfoRequest/PackageInfoResponse: Get package metadata. Only components delivered as files (ComponentType.IsFileComponent()) have a package to request.DownloadPackageRequest: Request package downloadsSignatureValue: Cryptographic signature validation
SupportErrorRequest/SupportErrorResponse: Automated error reporting, optionally with per-componentLogsSupportIssueRequest/SupportIssueResponse: Manual issue creation with AI; the response'sIssueIDis what laterSupportInvestigationRequestcalls addressSupportLogs: Component log collectionSupportInvestigationRequest/SupportInvestigationResponse: AI-powered troubleshooting. SetUseStream: trueand bind a streaming call type (CallReqBytesRespReader/Writer) to receive the answer as Server-Sent Events instead of one JSON response.SupportMsgLog/MsgLogType: One message of the (anonymised) investigation conversation, returned inSupportInvestigationResponse.MsgLogs.MsgLogTypeWaitis a real value you will see: the server sends it (with a translated "please wait" message) when an investigation has to wait for the issue's background log analysis to finish first — no retry needed, the same call resolves once the wait is over.
system.GetInstallationID(): Generates stable, machine-specific UUID for installation tracking
- Basic error reporting: Automated error submission
- Package validation: Ed25519 signature verification
- Rate limiting: Standard PoW difficulty
- AI troubleshooting: x5 investigation sessions/day
- Package downloads: Access to all packages
- Rate limiting: Reduced PoW difficulty
- Advanced AI troubleshooting: x50 investigation sessions/day
- Custom integrations: Specialized endpoints and workflows
- Priority processing: Minimal PoW difficulty and fast-track handling
The VXControl Cloud Platform is actively expanding. Future releases may include:
- Threat Intelligence Services: IOC/IOA database access and threat analysis
- Vulnerability Assessment: CVE database integration and security scanning
- Computational Resources: Cloud-based intensive task processing
- Advanced Analytics: Security metrics and reporting dashboards
- Custom Workflows: Specialized security automation pipelines
Note: These features are in development and not yet available in the current SDK version.
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'feat: add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Documentation: API Reference | Examples
- Issues: GitHub Issues
- Enterprise Support: support@vxcontrol.com
- Community: Discord and Telegram
The VXControl Cloud SDK code is licensed under the MIT License.
Copyright (c) 2026 PentAGI Development Team
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
What this means:
- ✅ Free to use in any project (open source, commercial, proprietary)
- ✅ No licensing fees for the SDK code itself
- ✅ Modify and distribute freely with attribution
- ✅ Integrate into commercial products without restrictions
See LICENSE for complete MIT license terms.
What requires a License Key:
- 🔑 API Access to VXControl Cloud Platform services
- 🔑 Threat Intelligence data and updates
- 🔑 AI-Powered Support and troubleshooting assistance
- 🔑 Package Downloads from secure repositories
- 🔑 Premium Features and enterprise capabilities
Service Tiers:
- Free Tier: Basic error reporting and package validation
- Professional Tier: AI troubleshooting, package downloads
- Enterprise Tier: Full threat intelligence, priority support
Usage Restrictions: Cloud services and obtained data may ONLY be used for:
- ✅ Defensive cybersecurity and authorized security testing
- ✅ Academic research and education in controlled environments
- ✅ Incident response and compliance assessment
- ❌ Prohibited: Unauthorized access, malicious activities, or illegal purposes
Get Started:
- Use the SDK: MIT licensed code works immediately
- Get a License Key: Register at console.pentagi.com to obtain a license key for cloud services access
- Review Terms: Read TERMS_OF_SERVICE.md before using cloud services
For license keys and account management: console.pentagi.com
For licensing questions: info@vxcontrol.com
For Terms of Service violations: info@vxcontrol.com (Subject: "Cloud Services Terms")