The official Go SDK for MIOSA. Requires Go 1.21+. All methods accept a context.Context for cancellation and timeouts.
Install
go get github.com/Miosa-osa/miosa-go@v0.4.0 What’s new in v0.4.0
| Area | What shipped |
|---|---|
| Sandbox lifecycle | Sandboxes.Update(), Sandboxes.PreviewToken(), Sandboxes.Fork() |
| Tenant preview domain | Tenant.GetPreviewDomain(), SetPreviewDomain(), VerifyPreviewDomain(), DeletePreviewDomain() |
| Tenant branding | Tenant.GetBranding(), SetBranding(), DeleteBranding() |
| Webhooks | Webhooks.Create/List/Get/Delete/Test() |
| Files advanced | SandboxFiles.Tree(), WriteMany(), Watch() |
| Sandbox env | SandboxEnv.List/Set/Delete() |
| Processes | SandboxProcesses.Start/List/Get/Stop/Logs/Stream() |
| Templates + fork | SandboxTemplates.*, Sandboxes.Fork() |
| Usage / quotas | Usage.Get(), Quotas.Set/Get/Delete() |
| Sharing | SandboxShare.Create/List/Revoke() |
| Workspace members | WorkspaceMembers.*, WorkspaceInvites.* |
| Org invites | OrgInvites.* |
First request
package main
import (
"context"
"fmt"
miosa "github.com/Miosa-osa/miosa-go"
)
func main() {
ctx := context.Background()
client := miosa.NewClient("msk_live_...")
// Create a sandbox, run a command, delete it
sandbox, err := client.Sandboxes.Create(ctx, miosa.CreateSandboxInput{
Name: "quick-exec",
})
if err != nil {
panic(err)
}
result, err := client.Exec.Run(ctx, sandbox.ID, miosa.ExecParams{
Command: "python3 -c 'print(1 + 1)'",
})
if err != nil {
panic(err)
}
fmt.Println(result.Output) // "2\n"
_ = client.Sandboxes.Delete(ctx, sandbox.ID)
} Configure
| Option | Env var | Default | Description |
|---|---|---|---|
apiKey (positional) | MIOSA_API_KEY | - | Workspace key (msk_live_*) |
WithBaseURL(u) | - | https://api.miosa.ai/api/v1 | API endpoint |
WithTimeout(d) | - | 60s | Per-request timeout |
WithMaxRetries(n) | - | 3 | Automatic retries on 429 / 5xx |
WithHTTPClient(hc) | - | default http.Client | Custom HTTP client |
import (
"time"
miosa "github.com/Miosa-osa/miosa-go"
)
client := miosa.NewClient(
"msk_live_...",
miosa.WithBaseURL("https://api.miosa.ai/api/v1"),
miosa.WithTimeout(60 * time.Second),
miosa.WithMaxRetries(3),
) Authentication
Keys are passed as Authorization: Bearer <key>. The SDK validates the key format at construction time.
import "os"
client := miosa.NewClient(os.Getenv("MIOSA_API_KEY")) Error handling
Use errors.As to match typed errors:
import (
"errors"
"fmt"
miosa "github.com/Miosa-osa/miosa-go"
)
_, err := client.Computers.Get(ctx, "nonexistent-id")
if err != nil {
var notFound *miosa.NotFoundError
var rateLimit *miosa.RateLimitError
switch {
case errors.As(err, ¬Found):
fmt.Println("Computer not found")
case errors.As(err, &rateLimit):
fmt.Printf("Rate limited - retry after %.0fs\n", rateLimit.RetryAfter)
default:
fmt.Println("Error:", err)
}
} Streaming events
stream, err := computer.Events.Subscribe(ctx, miosa.EventSubscribeOptions{
Subscribe: []miosa.EventProducer{
miosa.ProducerWindow,
miosa.ProducerFile,
miosa.ProducerProcess,
},
Paths: []string{"/workspace"},
})
if err != nil {
panic(err)
}
defer stream.Close()
for ev := range stream.C {
fmt.Printf("[%s] %v\n", ev.Type, ev.Payload)
} The stream.C channel is closed when the connection closes or the context is cancelled.
Phase 1-5 API Reference
Sandbox lifecycle (Phase 1)
client.Sandboxes - *SandboxesService
// Create - full input struct
sandbox, err := client.Sandboxes.Create(ctx, miosa.CreateSandboxInput{
Name: "my-sandbox",
Slug: "acme-backend",
ExternalUserID: "usr_123",
ExternalProjectID: "proj_456",
ExternalWorkspaceID: "ws_789",
Metadata: map[string]interface{}{"env": "staging"},
TimeoutSec: 300,
AlwaysOn: false,
IdempotencyKey: uuid.NewString(),
})
sandbox, err = client.Sandboxes.Get(ctx, sandboxID)
list, err := client.Sandboxes.List(ctx, miosa.SandboxListParams{State: "running"})
err = client.Sandboxes.Delete(ctx, sandboxID)
// Update mutable fields
updated, err := client.Sandboxes.Update(ctx, sandboxID, miosa.UpdateSandboxInput{
Name: "updated-name",
Slug: "new-slug",
TimeoutSec: 600,
AlwaysOn: true,
})
// Mint a short-lived preview token
token, err := client.Sandboxes.PreviewToken(ctx, sandboxID, miosa.PreviewTokenInput{
ExpiresIn: 3600,
Scope: "read",
})
// token.Token, token.URL, token.ExpiresAt
// Fork from a snapshot or running sandbox
forked, err := client.Sandboxes.Fork(ctx, sandboxID, miosa.ForkSandboxInput{
SnapshotID: "snap_abc",
Name: "forked-sandbox",
ExternalUserID: "usr_123",
}) | Field | Type | Description |
|---|---|---|
Name | string | Display name |
Slug | string | URL-safe identifier |
ExternalUserID | string | White-label user attribution |
ExternalProjectID | string | White-label project attribution |
ExternalWorkspaceID | string | White-label workspace attribution |
Metadata | map[string]interface{} | Arbitrary key-value pairs |
TimeoutSec | int | Lifetime in seconds |
AlwaysOn | bool | Stay alive past idle timeout |
IdempotencyKey | string | Deduplication key for retries |
Tenant preview domain (Phase 1)
client.Tenant - *TenantService
// Get current custom preview domain
info, err := client.Tenant.GetPreviewDomain(ctx)
// info.Domain, info.VerifiedAt, info.CnameTarget
// Set custom preview domain
updated, err := client.Tenant.SetPreviewDomain(ctx, "preview.acme.com")
// Trigger DNS verification
result, err := client.Tenant.VerifyPreviewDomain(ctx)
// result["verified"], result["target"], result["records"]
// Remove custom domain
err = client.Tenant.DeletePreviewDomain(ctx) Tenant branding (Phase 1)
client.Tenant - *TenantService
// Get current branding
branding, err := client.Tenant.GetBranding(ctx)
// Set branding
updated, err := client.Tenant.SetBranding(ctx, miosa.BrandingData{
ProductName: "Acme AI",
LogoURL: "https://cdn.acme.com/logo.png",
SupportURL: "https://acme.com/support",
SupportEmail: "help@acme.com",
PrimaryColor: "#ff6600",
BackgroundColor: "#ffffff",
})
// Reset to platform defaults
err = client.Tenant.DeleteBranding(ctx) Webhooks (Phase 1)
client.Webhooks - *WebhooksService
// Create a webhook
wh, err := client.Webhooks.Create(ctx, miosa.CreateWebhookInput{
URL: "https://example.com/webhooks/miosa",
Events: []string{"sandbox.created", "sandbox.ready", "sandbox.destroyed"},
})
list, err := client.Webhooks.List(ctx)
wh, err = client.Webhooks.Get(ctx, whID)
err = client.Webhooks.Delete(ctx, whID)
err = client.Webhooks.Test(ctx, whID) Supported events: sandbox.created, sandbox.ready, sandbox.destroyed, sandbox.error, computer.started, computer.stopped, computer.error
Files advanced (Phase 2)
client.SandboxFiles(sandboxID) - *SandboxFilesService
files := client.SandboxFiles(sandboxID)
// Recursive directory tree
tree, err := files.Tree(ctx, "/workspace", 5) // path, depth
// Batch file write
result, err := files.WriteMany(ctx, []miosa.WriteFileInput{
{Path: "/workspace/app.go", ContentBase64: base64.StdEncoding.EncodeToString([]byte("package main"))},
})
// Watch for file-system changes (SSE channel)
events, err := files.Watch(ctx)
if err != nil {
panic(err)
}
for ev := range events {
fmt.Println(ev.Type, ev.Data)
} Sandbox environment variables (Phase 2)
client.SandboxEnv(sandboxID) - *SandboxEnvService
env := client.SandboxEnv(sandboxID)
vars, err := env.List(ctx)
_, err = env.Set(ctx, []miosa.EnvVar{{Key: "DATABASE_URL", Value: "postgres://..."}})
err = env.Delete(ctx, "DATABASE_URL") Processes (Phase 2)
client.SandboxProcesses(sandboxID) - *SandboxProcessesService
procs := client.SandboxProcesses(sandboxID)
// Start a long-running background process
proc, err := procs.Start(ctx, miosa.StartProcessInput{
Command: "npm run dev",
Env: map[string]string{"NODE_ENV": "development"},
Name: "dev-server",
})
list, err := procs.List(ctx)
one, err := procs.Get(ctx, proc.PID)
err = procs.Stop(ctx, proc.PID)
logs, err := procs.Logs(ctx, proc.PID, 100)
// Streaming process output (SSE channel)
stream, err := procs.Stream(ctx, proc.PID)
for ev := range stream {
fmt.Print(ev.Data)
} Usage and quotas (Phase 3)
client.Usage - client.Quotas
// Usage rollup
report, err := client.Usage.Get(ctx, miosa.UsageParams{
ExternalUserID: "usr_123",
GroupBy: "external_user_id",
Period: "30d",
})
// Set per-user quotas
quota, err := client.Quotas.Set(ctx, "usr_123", miosa.QuotaInput{
MaxSandboxes: 5,
MaxConcurrent: 2,
MaxStorageGB: 10,
MaxCreditCents: 10000,
})
quota, err = client.Quotas.Get(ctx, "usr_123")
err = client.Quotas.Delete(ctx, "usr_123") Audit log (Phase 3)
client.AuditLog
var after string
for {
page, err := client.AuditLog.List(ctx, miosa.AuditLogListParams{
After: after,
Limit: 50,
})
if err != nil {
break
}
for _, entry := range page.Items {
fmt.Println(entry.Action, entry.CreatedAt)
}
if page.NextCursor == "" {
break
}
after = page.NextCursor
} Sharing (Phase 4)
client.SandboxShare(sandboxID) - *SandboxShareService
share := client.SandboxShare(sandboxID)
// Create a public share URL (no API key required to access)
expiresIn := 3600
data, err := share.Create(ctx, miosa.CreateShareInput{
ExpiresIn: &expiresIn,
Scope: "read",
})
// data.ShareID, data.ShareURL, data.ExpiresAt
shares, err := share.List(ctx)
err = share.Revoke(ctx, shareID) White-label integrator flow
package main
import (
"context"
"fmt"
"os"
"github.com/google/uuid"
miosa "github.com/Miosa-osa/miosa-go"
)
func main() {
ctx := context.Background()
client := miosa.NewClient(os.Getenv("MIOSA_API_KEY"))
// 1. Configure tenant branding once
client.Tenant.SetBranding(ctx, miosa.BrandingData{
ProductName: "Acme AI",
LogoURL: "https://cdn.acme.com/logo.png",
PrimaryColor: "#ff6600",
})
// 2. Set and verify a custom preview domain
client.Tenant.SetPreviewDomain(ctx, "preview.acme.com")
client.Tenant.VerifyPreviewDomain(ctx)
// 3. Register webhook
client.Webhooks.Create(ctx, miosa.CreateWebhookInput{
URL: "https://api.acme.com/webhooks/miosa",
Events: []string{"sandbox.created", "sandbox.ready", "sandbox.destroyed"},
})
// 4. Set per-user quotas
client.Quotas.Set(ctx, "usr_dr_smith", miosa.QuotaInput{
MaxSandboxes: 3,
MaxConcurrent: 1,
})
// 5. Create a sandbox attributed to a user + project
sandbox, err := client.Sandboxes.Create(ctx, miosa.CreateSandboxInput{
Name: "smile-dental",
ExternalUserID: "usr_dr_smith",
ExternalProjectID: "proj_landing_page",
ExternalWorkspaceID: "ws_smile_dental",
Metadata: map[string]interface{}{"plan": "pro"},
IdempotencyKey: uuid.NewString(),
})
if err != nil {
panic(err)
}
// 6. Write files
import64 := func(s string) string {
return base64.StdEncoding.EncodeToString([]byte(s))
}
client.SandboxFiles(sandbox.ID).WriteMany(ctx, []miosa.WriteFileInput{
{Path: "/workspace/index.html", ContentBase64: import64("<h1>Hello</h1>")},
})
// 7. Mint a preview token
expiresIn := 3600
token, _ := client.Sandboxes.PreviewToken(ctx, sandbox.ID, miosa.PreviewTokenInput{
ExpiresIn: expiresIn,
Scope: "read",
})
fmt.Println("Preview URL:", token.URL)
// 8. Create a public share link
shareExpiresIn := 86400
share, _ := client.SandboxShare(sandbox.ID).Create(ctx, miosa.CreateShareInput{
ExpiresIn: &shareExpiresIn,
Scope: "read",
})
fmt.Println("Public URL:", share.ShareURL)
// 9. Query usage
report, _ := client.Usage.Get(ctx, miosa.UsageParams{
ExternalUserID: "usr_dr_smith",
Period: "30d",
})
fmt.Println(report)
// 10. Clean up
client.Sandboxes.Delete(ctx, sandbox.ID)
} Common patterns
Context-based timeouts
ctx, cancel := context.WithTimeout(context.Background(), 90*time.Second)
defer cancel()
result, err := client.Exec.Run(ctx, computerID, miosa.ExecParams{
Command: "npm run build",
Timeout: 80,
}) Idempotency key
import "github.com/google/uuid"
sandbox, err := client.Sandboxes.Create(ctx, miosa.CreateSandboxInput{
Name: "my-sandbox",
IdempotencyKey: uuid.NewString(),
}) Custom base URL (self-hosted)
client := miosa.NewClient(
"msk_live_...",
miosa.WithBaseURL("https://api.your-domain.com/api/v1"),
) Resource coverage
| Resource group | Available |
|---|---|
| Sandboxes (create, get, list, delete, update, previewToken, fork) | ✓ |
| Sandbox files (tree, writeMany, watch) | ✓ |
| Sandbox env, processes, share | ✓ |
| Computers (CRUD + lifecycle) | ✓ |
| Computer sub-resources (terminal, env, ports, volumes, logs, auto-stop, metrics) | ✓ |
| Desktop | ✓ |
| Files (upload, download, list, delete) | ✓ |
| Exec | ✓ |
| Deployments | ✓ |
| Databases (managed Postgres) | ✓ |
| Storage (object storage) | ✓ |
| Volumes | ✓ |
| Custom domains | ✓ |
| Functions / cron jobs / health checks / webhooks | ✓ |
| Sandbox templates | ✓ |
| API keys | ✓ |
| Tenant (plan, preview domain, branding) | ✓ |
| Regions / settings | ✓ |
| Dashboard / analytics / audit log / usage / quotas | ✓ |
| Channels / integrations | ✓ |
| External keys | ✓ |
| MCP | ✓ |
| Models / completions / embeddings | ✓ |
| Command center / builder sessions / community | ✓ |
| OpenComputers (hosts, jobs, files, tunnels, agents, clusters) | ✓ |
| Workspace members + invites | ✓ |
| Org invites | ✓ |
| Admin | ✓ |
| Snapshots (standalone) | ✓ |
| Credits | ✓ |