Multiple Clients & Profile Management
Without profiles, working across clients means re-authenticating every time you switch, and one forgotten switch means deploying client A’s code into client B’s account. Profiles solve this by giving each client a named, credential-bearing entry in ~/.satusky/profiles/. Switching is one command, and a --profile flag lets you run a single command against any client without changing the active profile at all.
How profiles work
Section titled “How profiles work”Two locations matter:
| Location | Contents |
|---|---|
~/.satusky/profiles/<name>.json | Named profile — API URL, auth token, active org |
~/.satusky/context.json | Which profile name is currently active |
1ctl profile use <name> writes the profile name to context.json. Every subsequent command reads context.json to know which credentials and API URL to use. --profile <name> (or the equivalent SATUSKY_PROFILE=<name> env var) overrides the active profile for that one command only — context.json is not touched. These one-shot overrides are safe for parallel CI jobs that share a filesystem, because no job can stomp another’s active profile.
Profile vs org: a profile holds API URL and credentials (who you are). An org is a workspace within that account (where you deploy). Both are stored in context.json. Switching profile does not automatically switch org — set both explicitly.
Step 1: Create profiles for each client
Section titled “Step 1: Create profiles for each client”1ctl profile create --url https://api.satusky.com/v1/cli client-a1ctl profile create --url https://api.satusky.com/v1/cli client-bEach command confirms creation and prints the next steps:
✅ Profile 'client-a' createdAPI URL: https://api.satusky.com/v1/cli💡 Next steps:💡 1ctl profile use client-a💡 1ctl auth login --token=<your-token>Each new profile is unauthenticated. Authenticate each one by switching to it and running 1ctl auth login --token:
1ctl profile use client-a1ctl auth login --token=<client-a-api-token>
1ctl profile use client-b1ctl auth login --token=<client-b-api-token>Get API tokens from https://cloud.satusky.com/<org-id>/token. You can also pass the token via the SATUSKY_API_KEY environment variable instead of the --token flag.
Step 2: Verify all profiles are registered
Section titled “Step 2: Verify all profiles are registered”1ctl profile listProfiles──────── client-a API URL: https://api.satusky.com/v1/cli Auth: [email protected] Org: client-a-org--- client-b API URL: https://api.satusky.com/v1/cli Auth: [email protected] Org: client-b-org---* local API URL: http://localhost:8080/v1/cli Auth: [email protected] Org: myorg---The * marks the currently active profile. Profiles that have not been authenticated yet show Auth: (not logged in) and no Org line.
Check the active profile at a glance:
1ctl profile currentActive Profile──────────────Profile: localAPI URL: http://localhost:8080/v1/cliAuth: [email protected]Org: myorgStep 3: Project layout
Section titled “Step 3: Project layout”Each client project has its own satusky.toml. The config file contains only app shape — name, port, resources. No credentials, no profile name. Credentials live in ~/.satusky/profiles/, separate from source control.
~/projects/ client-a-dashboard/ satusky.toml ← name="client-a-dashboard", port=3000 src/ Dockerfile client-b-shop/ satusky.toml ← name="client-b-shop", port=8080 src/ DockerfileCommit satusky.toml. Never commit ~/.satusky/.
Step 4: Daily workflow — switch and deploy
Section titled “Step 4: Daily workflow — switch and deploy”Switch to the client you’re working on, do your work, switch back when done.
# Morning: starting on client A1ctl profile use client-a✅ Switched to profile 'client-a'API URL: https://api.satusky.com/v1/cli1ctl deploy --config ~/projects/client-a-dashboard/satusky.toml --wait💡 Packaging build context...💡 Submitting build to cloud...Step 1/5: Building image (cloud) client-a-dashboard ✓Step 2/5: Creating/updating deployment client-a-dashboard ✓Step 3/5: Configuring services client-a-dashboard ✓Step 4/5: Setting up environment and storage client-a-dashboard ✓Step 5/5: Configuring public routing and dependencies client-a-dashboard ✓💡 Generated new domain: goldenotter-3c2d1e0.satusky.com✅ 🚀 Deployment for client-a-dashboard is successful! Your app is live at: https://goldenotter-3c2d1e0.satusky.comDeployment ID: 3c2d1e0f-5f87-4612-b306-3da846b95d18💡 Waiting for deployment to become healthy...✅ Deployment is healthy — pods Running# Afternoon: switching to client B1ctl profile use client-b✅ Switched to profile 'client-b'API URL: https://api.satusky.com/v1/cli1ctl deploy --config ~/projects/client-b-shop/satusky.toml --waitAfter this deploy, the active profile is client-b — subsequent commands talk to client B’s account until you switch again.
Step 5: One-shot command without switching — the --profile flag
Section titled “Step 5: One-shot command without switching — the --profile flag”--profile <name> (short: -p <name>) applies to that one command only. The active profile in context.json is not touched.
# Active profile is client-a. Check client B's deployments without switching.1ctl --profile client-b deploy list# Deploy client B without changing the active profile.1ctl --profile client-b deploy \ --config ~/projects/client-b-shop/satusky.toml --waitAfter either command, 1ctl profile current still shows client-a.
The SATUSKY_PROFILE environment variable is equivalent to --profile — useful in shell scripts and Makefiles:
SATUSKY_PROFILE=client-b 1ctl deploy listStep 6: Switch org within a profile
Section titled “Step 6: Switch org within a profile”Clients with staging and production environments are typically separate orgs under the same Satusky account. Use 1ctl org switch to move between them.
List available orgs for the current profile:
1ctl org listOrganizations─────────────ID: b322955e-6a86-4157-8bff-1bea605ef8ac (current)Name: client-a-prodCreated: 3 weeks ago---ID: 690839ba-3aed-47ea-a8ec-0cd019e4d180Name: client-a-stagingCreated: 1 month ago---Switch by name or by UUID:
1ctl org switch client-a-staging✅ Switched to organization: client-a-stagingOrganization: client-a-stagingOrganization ID: 690839ba-3aed-47ea-a8ec-0cd019e4d1801ctl org switch 690839ba-3aed-47ea-a8ec-0cd019e4d180✅ Switched to organization: client-a-stagingOrganization: client-a-stagingOrganization ID: 690839ba-3aed-47ea-a8ec-0cd019e4d180Check the active org at any time:
1ctl org currentCurrent Organization────────────────────Organization: client-a-stagingOrganization ID: 690839ba-3aed-47ea-a8ec-0cd019e4d180Namespace: client-a-staging-690839baExample: deploy the same app to staging then production:
1ctl profile use client-a
1ctl org switch client-a-staging1ctl deploy --config ~/projects/client-a-dashboard/satusky.toml --wait
1ctl org switch client-a-prod1ctl deploy --config ~/projects/client-a-dashboard/satusky.toml --waitStep 7: Status check across all clients
Section titled “Step 7: Status check across all clients”Check each client independently with --profile and -o json. The field for the deployment name is app_label:
# Client A1ctl --profile client-a -o json deploy list | jq '.[] | {app_label, status}'{ "app_label": "client-a-dashboard", "status": "completed" }{ "app_label": "client-a-api", "status": "completed" }# Client B1ctl --profile client-b -o json deploy list | jq '.[] | {app_label, status}'{ "app_label": "client-b-shop", "status": "completed" }{ "app_label": "client-b-workers", "status": "completed" }Neither command changes the active profile.
Step 8: Confirm authenticated state
Section titled “Step 8: Confirm authenticated state”1ctl auth status✅ Authenticated with Satusky
User Email: [email protected]Organization: client-a-prodOrganization ID: 3c2d1e0f-5f87-4612-b306-3da846b95d18Namespace: client-a-prod-3c2d1e0fToken expires: in 71 daysRun this after switching profiles if you want to confirm which account you’re acting as.
Step 9: Recover from a deploy to the wrong client
Section titled “Step 9: Recover from a deploy to the wrong client”It happens. You forgot to switch profiles and deployed client A’s dashboard into client B’s account.
Detect it:
1ctl --profile client-b -o json deploy list | jq '.[] | {app_label, status}'{ "app_label": "client-b-shop", "status": "completed" }{ "app_label": "client-a-dashboard", "status": "completed" }client-a-dashboard should not be there. Destroy it:
1ctl --profile client-b deploy destroy \ --config ~/projects/client-a-dashboard/satusky.toml \ -yThen redeploy to the correct profile:
1ctl --profile client-a deploy \ --config ~/projects/client-a-dashboard/satusky.toml --waitStep 10: Delete a profile
Section titled “Step 10: Delete a profile”When a client engagement ends:
1ctl profile delete client-a✅ Profile 'client-a' deletedUsing SATUSKY_PROFILE in a Makefile
Section titled “Using SATUSKY_PROFILE in a Makefile”SATUSKY_PROFILE=<name> scopes the profile override to a single shell command without writing to context.json. This makes it safe for parallel CI jobs that share a filesystem — no job can overwrite another’s active profile.
# Makefile.PHONY: deploy-client-b-staging deploy-client-b-prod
deploy-client-b-staging: SATUSKY_PROFILE=client-b 1ctl org switch client-b-staging && \ SATUSKY_PROFILE=client-b 1ctl deploy \ --config ~/projects/client-b-shop/satusky.toml --wait
deploy-client-b-prod: SATUSKY_PROFILE=client-b 1ctl org switch client-b-prod && \ SATUSKY_PROFILE=client-b 1ctl deploy \ --config ~/projects/client-b-shop/satusky.toml --waitTwo CI jobs running make deploy-client-b-staging and make deploy-client-a-dashboard in parallel will never interfere because neither touches the shared context.json.
Run 1ctl profile list at the start of every terminal session. One glance confirms which client you’re acting as before you type anything else.
Keep satusky.toml in source control; keep ~/.satusky/ out of it. The config file is safe to commit — it has no credentials. Profile data is machine-local and must never be committed.
Use SATUSKY_PROFILE or --profile in CI, not profile use. profile use writes to context.json, a shared file. In parallel CI jobs, jobs can overwrite each other’s active profile. The env var and flag scope the override to a single process.
Consider naming profiles client-a-staging and client-a-prod rather than using one client-a profile and switching orgs manually. One profile per deployment target is less error-prone and makes --profile flags unambiguous.
Quick reference
Section titled “Quick reference”| Task | Command |
|---|---|
| Create a profile | 1ctl profile create --url <url> <name> |
| List all profiles | 1ctl profile list |
| Show active profile | 1ctl profile current |
| Switch active profile | 1ctl profile use <name> |
| Delete a profile | 1ctl profile delete <name> |
| One-off command on another profile | 1ctl --profile <name> <command> |
| Override profile in a script/Makefile | SATUSKY_PROFILE=<name> 1ctl <command> |
| List orgs | 1ctl org list |
| Switch org | 1ctl org switch <org-name-or-uuid> |
| Show active org | 1ctl org current |
| Confirm authenticated state | 1ctl auth status |
| Destroy a stray deployment | 1ctl --profile <name> deploy destroy --config <path> -y |