env-master
Documentation sections

Quickstart

From an empty install to a service that reads its database password itself. Every step is a working command; variables introduced in one step are used in the next ones.

1. Create an account

The service runs at https://env.aistastudio.ru — nothing to install or maintain. Sign up and confirm your email via the link in the message. Registration creates a personal organization — it becomes the owner of projects, members, and the plan.

The API address is the same domain with the /api/v1 prefix:

export ENVMASTER_URL=https://env.aistastudio.ru
BASE=$ENVMASTER_URL/api/v1

2. Log in and find your organization

TOKEN=$(curl -s $BASE/auth/login -H 'content-type: application/json' \
  -d '{"email":"вы@example.com","password":"..."}' | jq -r .accessToken)

ORG=$(curl -s $BASE/orgs -H "authorization: Bearer $TOKEN" | jq -r .items[0].id)

A user access token lives for 15 minutes — it will expire right in the middle of this guide; just log in again.

3. Create a project and an environment

PROJECT=$(curl -s $BASE/projects -H "authorization: Bearer $TOKEN" \
  -H 'content-type: application/json' \
  -d "{\"orgId\":\"$ORG\",\"name\":\"Billing API\",\"slug\":\"billing-api\"}" | jq -r .id)

ENV=$(curl -s $BASE/projects/$PROJECT/environments -H "authorization: Bearer $TOKEN" \
  -H 'content-type: application/json' \
  -d '{"slug":"production"}' | jq -r .id)

No environments are created by default: dev, staging, production are your choice, not a preset. The project creator automatically gets the admin role in it, so the next steps need no extra permissions.

4. Store a secret

curl -s "$BASE/projects/$PROJECT/entries?env=production" \
  -X PUT -H "authorization: Bearer $TOKEN" -H 'content-type: application/json' \
  -d '{"key":"DB_PASSWORD","kind":"secret","visibility":"masked","value":"s3cret"}'

A secret must be masked or restricted; a variable — only plain. Key names are uppercase letters, digits, and underscores, starting with a letter.

5. Create a service

A service is what you deploy; without it there is nothing to grant credentials to:

SERVICE=$(curl -s $BASE/projects/$PROJECT/services -H "authorization: Bearer $TOKEN" \
  -H 'content-type: application/json' \
  -d '{"name":"Billing worker","slug":"billing-worker"}' | jq -r .id)

6. Issue an identity to the service

An identity is the credentials of one service in one environment. Here — universal mode with a permanent client secret:

curl -s $BASE/services/$SERVICE/identities -H "authorization: Bearer $TOKEN" \
  -H 'content-type: application/json' \
  -d "{\"name\":\"prod worker\",\"environmentId\":\"$ENV\",\"mode\":\"universal\",\"allowedKeys\":[\"DB_*\"]}"

The response contains clientId and clientSecret — the secret is shown exactly once. Save both: they go into the service's environment variables next. A lost secret is not recovered; a new identity is issued.

If issuance goes through CI and the client secret ends up in logs or an image along the way, use the enrollment mode instead — a one-time token the service exchanges for permanent credentials on its first start: Services and identities.

7. Connect the service

npm i @env-master/sdk    # or bun add @env-master/sdk
import { EnvMasterClient } from "@env-master/sdk";

const em = new EnvMasterClient({
  baseUrl: process.env.ENVMASTER_URL!,
  project: "billing-api",
  environment: "production",
});

await em.loginMachine({
  clientId: process.env.ENVMASTER_CLIENT_ID!,
  clientSecret: process.env.ENVMASTER_CLIENT_SECRET!,
});

const password = await em.get("DB_PASSWORD");

Done. From here the value can be changed in the dashboard — the service will pick up the current one on its next start, and with rotation without a restart at all.

Migrating an existing .env

ENVMASTER_TOKEN=... envmaster import .env \
  --project billing-api --env production --dry-run

The command decides on its own what looks like a secret and shows the plan before writing. Details — in the CLI.