Skip to content

1ctl v1 Contract

This page is the product and engineering contract for 1ctl v1. It complements 1ctl --help: help output lists commands and flags, while this page explains the expected behavior, current implementation status, and gaps that should be closed before the CLI feels reliable for day-to-day deployment work.

Status labels used in this page:

LabelMeaning
ImplementedPresent in the current CLI/backend path and verified in normal use.
Partially implementedUsable, but behavior or cleanup is incomplete.
Known gapCurrent behavior can mislead users, leave drift, or lacks an expected operational command.
V1 targetRecommended behavior for a robust version 1, based on common PaaS CLI patterns such as flyctl, railway, and render.

1ctl should let a developer deploy, inspect, debug, scale, and destroy an app without knowing Kubernetes. It can expose Kubernetes concepts when they help explain behavior, but its primary contract is user-facing:

  1. 1ctl deploy creates or updates an application.
  2. 1ctl deploy --wait tells the truth about what became ready.
  3. 1ctl deploy get/status show the current source of truth.
  4. 1ctl logs and 1ctl logs stream make debugging possible immediately.
  5. 1ctl deploy destroy removes what the platform created, or clearly says what remains.

The current 1ctl --help command surface includes:

CommandStatusPurpose
authImplementedLogin, logout, and auth status.
profileImplementedNamed API endpoints and credentials.
org / organizationImplementedOrganization selection and listing.
initImplementedCreate satusky.toml.
launchImplementedInteractive project detection and config creation.
deployImplementedBuild and deploy apps; manage releases, rollback, restart, scale, destroy.
secretImplementedManage encrypted deployment secrets.
env / environmentImplementedManage non-sensitive environment variables.
domains / domainPartially implementedAttach, list, remove, and check domains. Route reconciliation needs hardening.
machineImplementedList owned machines, available machines, and usage records.
logsImplementedFetch recent logs and stream live pod logs.
credits / billingImplementedCredit balance and billing state.
notifications / notifImplementedNotification history and preferences.
userImplementedUser account details.
token / api-tokenImplementedAPI token lifecycle.
marketplace / market / appsImplementedBrowse and deploy marketplace apps.
auditImplementedAudit log queries.
pricing / priceImplementedMachine pricing information.
clusterImplementedCluster and zone information.
completionImplementedShell completions.

Docs may contain older platform or admin-oriented pages for services, ingress, issuers, object storage, Talos, or admin operations. Treat those as platform/internal or legacy unless they appear in your installed 1ctl --help.

Page / commandLabelCurrent guidance
cli/service / 1ctl serviceLegacy/manualServices are created by 1ctl deploy; do not rely on direct service commands unless your CLI exposes them. Tracked in satu-docs#1.
cli/ingress / 1ctl ingressLegacy/manualDefault deploys use Gateway API HTTPRoute; use 1ctl domains for domain work. Tracked in satu-docs#1.
cli/issuer / 1ctl issuerLegacy/manualTLS should be managed by the platform route/domain flow. Tracked in satu-docs#1.
cli/storage / 1ctl storageLegacy/not current helpThis page describes old S3 object storage commands. Persistent app data uses deploy volumes. Tracked in satu-docs#1, 1ctl#44, and 1ctl#47 for the malformed unknown-command error.
cli/talos / 1ctl talosInternalPlatform-operator node management, not app deployment. Tracked in satu-docs#1.
cli/admin / 1ctl adminInternalSuper-admin/platform operations, not regular user workflows. Tracked in satu-docs#1.

Implemented. 1ctl deploy --wait waits for pods to become healthy. A verified deployment can return:

Status: Running
Message: Deployment is running normally
Progress: 100%

Known gap. Pod readiness and public URL readiness are not the same thing. A deployment can have healthy pods while its generated URL is not resolvable or not attached to the live route.

Tracked in:

Observed failure mode:

  • Backend recorded happygiraffe-3u7lttk.satusky.com.
  • Kubernetes HTTPRoute still contained silentgiraffe-6z2enyu.satusky.com.
  • 1ctl deploy --wait timed out waiting for DNS, then still reported deployment success.
  • The old hostname remained live and routed to the new pod.

V1 target. 1ctl deploy --wait should report readiness in separate dimensions:

Deployment: healthy
Pods: Running
Public URL: not ready
Reason: DNS unresolved / route mismatch
URL: https://happygiraffe-3u7lttk.satusky.com

If --wait means “wait until the app is publicly reachable”, URL failure should produce a non-zero exit. If --wait means “wait until pods are ready”, the CLI should say so and avoid implying the URL is live.

Implemented. The CLI can list, add, remove, and check domains:

Terminal window
1ctl domains list
1ctl domains add api.example.com --app my-api --custom-dns
1ctl domains remove api.example.com --app my-api
1ctl domains check api.example.com

Known gap. Backend domain metadata can drift from Kubernetes routing state. The platform must treat the database and Kubernetes route as a single reconciled unit.

Tracked in:

V1 target.

  • deploy should create or update the HTTPRoute hostname that matches the domain returned by deploy get.
  • destroy should remove all owned public route resources or state exactly what remains.
  • domains check should show DB ownership, Kubernetes route attachment, DNS records, and TLS state separately.
  • Stale domains should return a real not-found or detached status, not an empty object with invalid timestamps.

Implemented. A deployment can request a persistent volume through flags:

Terminal window
1ctl deploy --volume-size 1Gi --volume-mount /app/data

The equivalent satusky.toml shape is:

[volume]
size = "1Gi"
mount = "/app/data"

Partially implemented. Volume creation is wired through deploy, but volume lifecycle commands are not yet as complete as mature CLIs. The CLI code currently registers volumes for cleanup visibility, while full backend delete support for deploy-created PVCs is still a gap.

Tracked in:

V1 target. Storage should have an explicit lifecycle comparable to fly volumes:

Terminal window
1ctl volumes list
1ctl volumes create --name data --size 1Gi
1ctl volumes attach --deployment-id <id> --mount /app/data
1ctl volumes detach --deployment-id <id>
1ctl volumes destroy <volume-id>

Until then, docs and CLI output must be careful: deploy destroy should not promise PVC deletion unless the backend has actually deleted the PVC.

Implemented.

Terminal window
1ctl logs --deployment-id <id> --tail 20
1ctl logs stream --deployment-id <id> --batch-size 5

1ctl logs supports --tail. 1ctl logs stream does not support --tail; it supports --batch-size.

V1 target. Both commands should make target resolution explicit: whether they used --deployment-id, satusky.toml, namespace/app flags, or current profile.

Tracked in 1ctl#45.

Implemented. JSON output is a global flag:

Terminal window
1ctl -o json deploy list

Known gap. Some users naturally try 1ctl deploy list -o json, but global flags are not consistently accepted after subcommands.

Tracked in 1ctl#42.

V1 target. Either support global flags in both positions or document the strict form everywhere:

Terminal window
1ctl -o json <command> <subcommand>

For CI, commands should return structured fields for:

  • deployment ID
  • image
  • pod status
  • public URL
  • public URL readiness
  • route hostname
  • DNS state
  • TLS state

Implemented. 1ctl deploy destroy deletes the deployment record and associated app resources for normal cases.

Known gap. A verified case showed a previous HTTPRoute hostname surviving destroy and routing traffic to a later deployment with the same app name.

Tracked in:

V1 target. Destroy should be idempotent and auditable. It should report each resource class:

Deployment: deleted
Service: deleted
HTTPRoute: deleted
DNS record: deleted or not owned
TLS certificate: deleted or retained
PVC: retained or deleted

For destructive resources such as PVCs, the CLI should make retention/deletion explicit instead of hiding it in a generic confirmation prompt.

For local backend testing, point the CLI at the local API route:

Terminal window
export SATUSKY_API_URL=http://localhost:8080/v1/cli

Profiles can persist the same endpoint:

Terminal window
1ctl profile create --url http://localhost:8080/v1/cli local
1ctl profile use local

The API URL resolution order is:

  1. --api-url
  2. SATUSKY_API_URL
  3. --profile / active profile
  4. production default

A release should be considered a good v1 when these checks pass consistently: