Skip to content

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.

Two locations matter:

LocationContents
~/.satusky/profiles/<name>.jsonNamed profile — API URL, auth token, active org
~/.satusky/context.jsonWhich 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.


Terminal window
1ctl profile create --url https://api.satusky.com/v1/cli client-a
1ctl profile create --url https://api.satusky.com/v1/cli client-b

Each command confirms creation and prints the next steps:

✅ Profile 'client-a' created
API 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:

Terminal window
1ctl profile use client-a
1ctl auth login --token=<client-a-api-token>
1ctl profile use client-b
1ctl 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”
Terminal window
1ctl profile list
Profiles
────────
client-a
API URL: https://api.satusky.com/v1/cli
Org: client-a-org
---
client-b
API URL: https://api.satusky.com/v1/cli
Org: client-b-org
---
* local
API URL: http://localhost:8080/v1/cli
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:

Terminal window
1ctl profile current
Active Profile
──────────────
Profile: local
API URL: http://localhost:8080/v1/cli
Org: myorg

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/
Dockerfile

Commit 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.

Terminal window
# Morning: starting on client A
1ctl profile use client-a
✅ Switched to profile 'client-a'
API URL: https://api.satusky.com/v1/cli
Terminal window
1ctl 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.com
Deployment ID: 3c2d1e0f-5f87-4612-b306-3da846b95d18
💡 Waiting for deployment to become healthy...
✅ Deployment is healthy — pods Running
Terminal window
# Afternoon: switching to client B
1ctl profile use client-b
✅ Switched to profile 'client-b'
API URL: https://api.satusky.com/v1/cli
Terminal window
1ctl deploy --config ~/projects/client-b-shop/satusky.toml --wait

After 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.

Terminal window
# Active profile is client-a. Check client B's deployments without switching.
1ctl --profile client-b deploy list
Terminal window
# Deploy client B without changing the active profile.
1ctl --profile client-b deploy \
--config ~/projects/client-b-shop/satusky.toml --wait

After either command, 1ctl profile current still shows client-a.

The SATUSKY_PROFILE environment variable is equivalent to --profile — useful in shell scripts and Makefiles:

Terminal window
SATUSKY_PROFILE=client-b 1ctl deploy list

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:

Terminal window
1ctl org list
Organizations
─────────────
ID: b322955e-6a86-4157-8bff-1bea605ef8ac (current)
Name: client-a-prod
Created: 3 weeks ago
---
ID: 690839ba-3aed-47ea-a8ec-0cd019e4d180
Name: client-a-staging
Created: 1 month ago
---

Switch by name or by UUID:

Terminal window
1ctl org switch client-a-staging
✅ Switched to organization: client-a-staging
Organization: client-a-staging
Organization ID: 690839ba-3aed-47ea-a8ec-0cd019e4d180
Terminal window
1ctl org switch 690839ba-3aed-47ea-a8ec-0cd019e4d180
✅ Switched to organization: client-a-staging
Organization: client-a-staging
Organization ID: 690839ba-3aed-47ea-a8ec-0cd019e4d180

Check the active org at any time:

Terminal window
1ctl org current
Current Organization
────────────────────
Organization: client-a-staging
Organization ID: 690839ba-3aed-47ea-a8ec-0cd019e4d180
Namespace: client-a-staging-690839ba

Example: deploy the same app to staging then production:

Terminal window
1ctl profile use client-a
1ctl org switch client-a-staging
1ctl deploy --config ~/projects/client-a-dashboard/satusky.toml --wait
1ctl org switch client-a-prod
1ctl deploy --config ~/projects/client-a-dashboard/satusky.toml --wait

Check each client independently with --profile and -o json. The field for the deployment name is app_label:

Terminal window
# Client A
1ctl --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" }
Terminal window
# Client B
1ctl --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.


Terminal window
1ctl auth status
✅ Authenticated with Satusky
User Email: [email protected]
Organization: client-a-prod
Organization ID: 3c2d1e0f-5f87-4612-b306-3da846b95d18
Namespace: client-a-prod-3c2d1e0f
Token expires: in 71 days

Run 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:

Terminal window
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:

Terminal window
1ctl --profile client-b deploy destroy \
--config ~/projects/client-a-dashboard/satusky.toml \
-y

Then redeploy to the correct profile:

Terminal window
1ctl --profile client-a deploy \
--config ~/projects/client-a-dashboard/satusky.toml --wait

When a client engagement ends:

Terminal window
1ctl profile delete client-a
✅ Profile 'client-a' deleted

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 --wait

Two 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.


TaskCommand
Create a profile1ctl profile create --url <url> <name>
List all profiles1ctl profile list
Show active profile1ctl profile current
Switch active profile1ctl profile use <name>
Delete a profile1ctl profile delete <name>
One-off command on another profile1ctl --profile <name> <command>
Override profile in a script/MakefileSATUSKY_PROFILE=<name> 1ctl <command>
List orgs1ctl org list
Switch org1ctl org switch <org-name-or-uuid>
Show active org1ctl org current
Confirm authenticated state1ctl auth status
Destroy a stray deployment1ctl --profile <name> deploy destroy --config <path> -y