Skip to content

Set up a project: sunlicense init ​

Run it in your project's folder. It needs no answers, so an AI agent can run it too:

bash
sunlicense init            # or, for agents and scripts:
sunlicense init --json

It does the whole setup, and running it again finds what it made instead of creating more:

  1. Signs in. Your saved account (or SUNLICENSE_TOKEN). Not signed in: in a terminal it asks whether to continue as a guest, sign in or sign up; without one (an agent's shell, CI) it creates a guest account with no email. Keep it later with sunlicense claim.
  2. Creates a product named after the project (from package.json, pom.xml, Gradle settings, pyproject.toml, Cargo.toml, composer.json, *.csproj, go.mod, fxmanifest.lua… or the folder), with a Standard policy (subscription, 2 machines, 365 days).
  3. Creates an API key for the app and issues one test license (customer [email protected], never emailed).
  4. Writes the keys to .env (.env.local for Next.js, Vite, Nuxt, SvelteKit and Astro, or when the project has one) as SUNLICENSE_API_KEY and SUNLICENSE_LICENSE_KEY, keeping everything else in the file and its line endings, and adds the file to .gitignore unless git already ignores it.
  5. Prints the integration: the SDK's install line and code for the project's language, or, for a language without an official SDK (Go, C/C++, Swift, Ruby, Dart, …), the HTTP request and how to read the answer. Rust gets the HTTP request too until its SDK has a release that validates.

Full keys are only ever written to the gitignored file. The output shows them masked (K7QX-XXXX-XXXX-XXXX, slk_ab12cd34…).

Then check it the way your app will. With no flags, sunlicense validate reads both keys from .env, and --release frees this machine's slot again afterwards, so both of the test license's 2 machines stay free for your app:

bash
sunlicense validate --release        # VALID, exit code 0
FlagDoes
--jsonPrint the JSON below instead of text
--name "Acme Editor"Product (and guest organization) name
--language pythonOverride detection: java, js, python, dotnet, php, lua, rust or http
--env-file config/.envWhere to write the keys, relative to the project
--dir path/to/projectRun for another folder
--guest / --no-guestAlways create a guest without asking / never create one (fail when not signed in)
--product <code>Use this existing product (code or name) instead of one named after the project, e.g. when the plan has no room for another
--no-test-licenseDon't issue a test license
--project-credentials auto|yes|noAlso keep a new guest's sign-in in the project (below)

Containers, Codespaces and CI ​

When the home folder won't survive a rebuild (a dev container, GitHub Codespaces, Gitpod, Replit, CI, or any container), a new guest account's sign-in is also saved to .sunlicense/credentials in the project (readable by you only, and added to .gitignore), and the CLI uses it whenever no account is signed in on the machine. Never commit it: it can manage the organization. --project-credentials yes or no decides it yourself.

Guest accounts ​

A guest account is a normal Free organization you own, without an email yet. Until it's claimed it can't send anything to other people or host files: no customer emails, webhooks, Discord posts, customer portal, custom domains or product downloads, and at most 10 licenses. An unclaimed guest account is deleted 30 days after it was last used (any CLI or API call, a dashboard visit, or license checks), and every command reminds you on stderr while it's a guest. More in Guest accounts.

Keep a guest account: sunlicense claim ​

bash
sunlicense claim             # opens the claim page and waits until you're done
sunlicense claim --no-open   # print the link, to open on another device

Add your email and a password on the page and confirm the email; everything in the organization stays, and the CLI's sign-in keeps working. Until you've confirmed it, commands remind you to check your inbox.

Already have an account? You don't need the claim page: sunlicense login moves every guest account this computer kept into the organization you sign in to, with keys and the project's API key unchanged (details). sunlicense claim does the same when you're signed in to your own account here too.

Open the dashboard: sunlicense open ​

For a guest, sunlicense open opens the claim page, where you add your email and a password to keep the account; Not now opens the dashboard signed in as the guest instead (both links work once, for 10 minutes). While a claim waits for you to confirm your email, it signs you in straight away. For other accounts it opens the dashboard as usual. --print prints the link instead.

From a guest account's third day, every command also prints one line with the date it will be deleted if it isn't used, and sunlicense claim.

Add the MCP server to your AI client: sunlicense mcp install ​

bash
sunlicense mcp install                    # finds the client you're using
sunlicense mcp install --client cursor    # or name it
sunlicense mcp install --client codex --print

It tells the client apart from its environment, the processes that started the command, or the project's config folders, and adds sunlicense mcp to that client's MCP servers without touching anything else in the file:

Client--clientWritten to
Claude Codeclaude-code.mcp.json
Cursorcursor.cursor/mcp.json
VS Code (Copilot agent mode)vscode.vscode/mcp.json
Gemini CLIgemini.gemini/settings.json
Zedzed.zed/settings.json
JetBrains (Junie)jetbrains.junie/mcp/mcp.json
Roo Coderoo.roo/mcp.json
Kirokiro.kiro/settings/mcp.json
Codexcodex~/.codex/config.toml (Codex has no project config)
Windsurfwindsurf~/.codeium/windsurf/mcp_config.json
ClineclineCline's settings in VS Code
Antigravity, Aider, othersantigravity, aider, otherNothing: prints what to paste

--scope user writes the client's user config instead, for every project. A different existing sunlicense entry is kept unless you add --force. No token is written: the server signs in as the CLI does, so sign in first (sunlicense init does). A password sign-in expires after 12 hours; for assistants use a guest account or sunlicense login --with-token. Installed through npm without a global install, the config runs npx -y @sunlicense/cli@latest mcp.

Web apps and browser builders (claude.ai, ChatGPT, Lovable, Bolt, Replit) can't run a local command: connect them to https://api.sunlicense.com/mcp instead (remote MCP). More on the tools: AI assistants (MCP).

JSON output ​

sunlicense init --json prints one JSON document. It's a contract for AI agents: fields are added over time, never renamed or removed, and schemaVersion changes only if that ever has to break.

json
{
  "ok": true,
  "schemaVersion": 1,
  "account": { "organization": "space-trader", "organizationId": "…", "guest": true, "created": true,
    "deleteAfter": "2026-11-29T10:00:00Z", "server": "https://api.sunlicense.com",
    "dashboard": "https://app.sunlicense.com", "credentials": ["…/sunlicense/config.json"] },
  "project": { "dir": "/home/me/space-trader", "name": "space-trader", "language": "python",
    "languageName": "Python", "detectedFrom": "pyproject.toml" },
  "product": { "id": "…", "name": "space-trader", "code": "space-trader", "created": true },
  "policy": { "id": "…", "name": "Standard", "terms": "Subscription · 2 machines · 365 days", "created": true },
  "apiKey": { "id": "…", "name": "space-trader", "masked": "slk_ab12cd34…", "created": true,
    "envVar": "SUNLICENSE_API_KEY", "file": ".env" },
  "testLicense": { "id": "…", "key": "K7QX-XXXX-XXXX-XXXX", "status": "ACTIVE", "customerEmail": "[email protected]",
    "expiresAt": "…", "created": true, "envVar": "SUNLICENSE_LICENSE_KEY", "file": ".env" },
  "envFile": { "path": ".env", "gitignored": true, "addedToGitignore": true, "tracked": false },
  "integration": {
    "kind": "sdk",
    "sdk": { "id": "python", "name": "Python", "install": "pip install sunlicense", "repo": "https://github.com/sunlicense-com/sunlicense-py",
      "code": "…", "note": "…", "usedFor": "…" },
    "http": { "method": "POST", "url": "https://api.sunlicense.com/api/v2/license/validate",
      "headers": { "Authorization": "Bearer <SUNLICENSE_API_KEY>", "Content-Type": "application/json" },
      "body": { "license": "<the customer's license key>", "host": "<a stable id for this machine>" },
      "valid": "HTTP 200 …", "rejected": "HTTP 400 …", "codes": ["VALID", "NOT_FOUND", "EXPIRED", "…"], "example": "curl …" },
    "rules": ["Read the API key from the SUNLICENSE_API_KEY environment variable …", "…"]
  },
  "analytics": { "productAnalyticsId": "pa_7Hk2mQ9xZr4LbN8vT1wYcD", "enabled": true, "snippet": "…",
    "docs": "https://docs.sunlicense.com/guide/product-analytics" },
  "test": { "command": "sunlicense validate --release", "expect": "VALID (exit code 0) …" },
  "docs": { "llms": "https://docs.sunlicense.com/llms.txt", "validation": "…", "sdks": "…", "offline": "…", "mcp": "…", "init": "…", "analytics": "…" },
  "next": [
    { "command": "sunlicense validate --release", "why": "…", "for": "agent" },
    { "command": "sunlicense mcp install", "why": "…", "for": "agent" },
    { "command": "sunlicense guide", "why": "…", "for": "human" },
    { "command": "sunlicense claim", "why": "…", "for": "human" }
  ],
  "warnings": ["Guest account space-trader is deleted in 30 days if it isn't used. …"]
}
FieldMeaning
integration.kindsdk when an official SDK has code for the language (integration.sdk is set), http otherwise (sdk is null), including Rust until its SDK can validate. http is always there as a fallback.
createdtrue when this run made it, false when it found an earlier one
analyticsThe product's product analytics: its public product analytics id (pa_…, not a secret: free builds ship it), whether reports are on, and a snippet that starts reporting (the Java SDK's reporter for Java projects, else one report over HTTP). Left out when the server has no product analytics or your role can't read product settings
envFile.trackedThe file is already committed to git; a warning says how to fix that
next[].foragent: run it now. human: tell the developer to run it
warningsSentences to pass on to the developer

On failure the exit code is non-zero and the document is {"ok": false, "schemaVersion": 1, "error": {"code": "…", "message": "…"}, "next": […]}, with code such as guest_unavailable (this server can't create guest accounts: sign up instead), guest_limit (today's guest accounts from this network are used up), plan_limit (the plan has no room for another product: the message names your products; run again with --product <code> to use one of them, or upgrade), product_not_found (--product names none of yours), unavailable (SunLicense couldn't be reached) or error.

sunlicense mcp install --json prints {"ok", "detected": [{"id", "by", "detail"}], "results": [{"client", "name", "scope", "file", "action", "snippet", "note"}], "server": {"command", "args"}, "remote", "warnings"}, where action is added, unchanged, replaced, kept or printed.

Send feedback

Found something broken, or have an idea? It goes straight to our board.

The first line becomes the title.
Images (optional, up to 5: paste, drop or capture)
Check each image before sending, and remove any that show something private.

We also receive this page's address and your browser version.

Ctrl Enter to send