# Introduction ### Welcome to Shelve Shelve is an open-source environment management platform designed to revolutionize how development teams handle their environment variables and secrets. Born from the frustration of sharing sensitive information through insecure channels, Shelve provides a secure, collaborative, and developer-friendly solution for managing environment configurations across projects and teams. ### Why Shelve? - Simple setup: one command to log in, another to inject secrets at runtime. - Team focused: built for modern development teams with collaboration and granular permissions. - Secure by default: per-project envelope [encryption](https://shelve.cloud/docs/core-features/encryption), OS-keychain token storage, [scoped API tokens](https://shelve.cloud/docs/core-features/tokens), and [audit logs](https://shelve.cloud/docs/core-features/audit-logs) on every write. - Open source: completely free and self-hostable, giving you full control over your data. - Developer experience: CLI-first, `.env`-free workflow with `shelve run`. ### Key features Environment management - Centralized variable management across projects and teams. - First-class support for `development`, `preview`, `production`, plus custom environments. - Per-project data encryption keys (DEKs) sealed by a platform KEK — rotate scope is a single project. - Secret references (`shelve://ENV/KEY`) in `.env.template` for cross-environment composition. Team collaboration - Role-based access control on teams and projects. - Scoped, expiring API tokens with optional IP allowlists. - Full audit trail of team, project, variable, and token changes. - Secure member invitations. Integrations - GitHub secrets synchronization. - One-click deployment using Coolify. - Email authentication with OTP codes. - OAuth authentication (GitHub, Google). - Runtime injection into any process, CI, or container via `shelve run`. Developer tools - CLI with `init`, `login`, `run`, `pull`, `push`, `create`, `generate`, `config`, `upgrade`. - Encrypted offline cache — work from a plane, get variables back online automatically. - AI-agent safety: `init` provisions `.cursorignore` / `.aiderignore` / `.codeiumignore` files and never exposes secrets to your coding agent. - Docker image and Vercel deploy button for self-hosting. ### Getting Started Ready to improve your environment management workflow? Head over to our [Quickstart](https://shelve.cloud/docs/getting-started/quickstart) guide to begin your journey with Shelve. ### Community and Support Shelve is built by developers for developers. We believe in the power of community and open source. Join our growing community: - Star us on [GitHub](https://github.com/hugorcd/shelve){rel=""nofollow""} - Follow updates on [Twitter](https://x.com/shelvecloud){rel=""nofollow""} - Report issues on [Github](https://github.com/hugorcd/shelve/issues){rel=""nofollow""} Whether you're a solo developer or part of a large team, Shelve adapts to your needs while maintaining security and simplicity at its core. # Quickstart ::steps{level="3"} ### Create an account Sign up for a Shelve account on [Shelve](https://app.shelve.cloud){rel=""nofollow""} :prose-icon{name="custom:shelve"} . Or if you prefer to self-host, follow the instructions in the [Self-hosting](https://shelve.cloud/docs/self-hosting/vercel) guide. Once you have an account, complete the onboarding process to create your first team (workspace). ![login](https://shelve.cloud/docs/login.png) ### Install the Shelve CLI package :::code-group{sync="pm"} ```bash [pnpm] pnpm add -D @shelve/cli ``` ```bash [yarn] yarn add -D @shelve/cli ``` ```bash [npm] npm install -D @shelve/cli ``` ```bash [bun] bun add -D @shelve/cli ``` ::: :::callout You can also install the CLI globally using the `-g / --global` flag of your package manager. ::: ### Connect the CLI to your account You will need a token to authenticate the CLI with your account. You can generate a token in your account settings on the [Shelve App](https://app.shelve.cloud/user/tokens){rel=""nofollow""}. Once you have your token, run the following command in your terminal: ```bash [terminal] shelve login ``` This will prompt you to enter your token. Once you have entered your token, you will be logged in and ready to use the CLI. :::card ::::accordion :::::accordion-item --- icon: i-lucide-badge-help label: Can I use the CLI with my self-hosted instance? --- Before running the `shelve login` command, you can set the URL of your self-hosted instance in a manually created `shelve.json` file in the root of your project. The file should look like this: ```json [shelve.json] { "url": "https://your-instance-url.com" } ``` ::::: :::::accordion-item --- icon: i-lucide-users label: Can i use the CLI with multiple accounts? --- No, the CLI only supports one account at a time. If you need to switch accounts, you will need to log out and log back in with the new account. ::::: :::::accordion-item --- icon: i-lucide-refresh-cw label: Can I use the CLI in a CI/CD pipeline, for example GitHub Actions? --- Yes. Create a scoped, expiring [API token](https://shelve.cloud/docs/core-features/tokens) and expose it as `SHELVE_TOKEN` in your CI secrets. The CLI reads it directly — no `shelve login` needed. Pair it with `shelve run -- ` to inject variables without writing a `.env` file on the runner. ::::: :::::accordion-item{icon="i-lucide-search" label="What if I forget my token?"} If you forget your token, you can always generate a new one in your account settings on the [Shelve App](https://app.shelve.cloud/user/tokens){rel=""nofollow""} . ::::: :::: ::: :::note{to="https://shelve.cloud/docs/cli/create"} You can now start using the CLI to manage your secrets. Check out the [CLI documentation](https://shelve.cloud/docs/cli) for more information. ::: :: # Audit Logs Every security-relevant action in Shelve is recorded in an append-only audit log. The feed is scoped to a team and visible to members with at least the required role. ## What gets logged Among others: - `team.create`, `team.update`, `team.delete`, `team.member.add`, `team.member.role.update`, `team.member.remove` - `project.create`, `project.update`, `project.delete` - `environment.create`, `environment.update`, `environment.delete` - `variable.create`, `variable.update`, `variable.delete`, `variable.pull`, `variable.sync.github` - `token.create`, `token.delete` Every entry stores: - the **actor** (`user` with an id, `token` with the non-secret prefix, or `system` for scheduled jobs); - the **IP** (respecting the `X-Forwarded-For` chain on serverless / edge hosts); - the **user agent** (truncated to 256 chars); - the **resource** type and id; - an opaque **metadata** JSON blob — for example `{ "scopes": { "permissions": ["read"] } }` on `token.create` or `{ "count": 4 }` on `variable.create`. ::callout{type="info"} Audit writes are fire-and-forget: they never block the originating request. If recording fails, the event is logged to the application logs and the request still succeeds. :: ## API Retrieve logs via the REST API: ```bash [terminal] curl https://app.shelve.cloud/api/teams//audit-logs \ -H "Authorization: Bearer $SHELVE_TOKEN" ``` ### Query parameters ::field-group :::field{default="50" name="limit" type="number"} Number of entries to return. Between 1 and 100. ::: :::field{name="cursor" type="number"} `id` of the last entry seen on the previous page. The API returns entries with smaller ids (newest first). ::: :::field{name="action" type="string"} Filter by action name (for example `variable.create` ). Exact match. ::: :: The response includes a `nextCursor` field — pass it back to fetch the next page until it is `null`. ## Retention Audit logs are retained indefinitely on the hosted instance. On self-hosted deployments you control the retention policy by pruning the `audit_logs` table directly. # Encryption Shelve treats every secret value as encrypted data until the very moment you need it. This page describes the encryption scheme, the key hierarchy, and what actually hits the database. ## Two-tier envelope encryption Secret values are never encrypted directly with the global server key. Instead Shelve uses an envelope scheme: ```text value ──seal──▶ ciphertext (key = DEK, per-project) DEK ──seal──▶ encryptedDek (key = KEK, platform-wide) ``` - **KEK** — *Key Encryption Key*. A single secret sourced from `NUXT_PRIVATE_ENCRYPTION_KEY` at boot. It never touches stored data directly; it is only used to seal and unseal DEKs. - **DEK** — *Data Encryption Key*. Generated server-side on a project's first write (32 random bytes, base64-encoded), sealed with the KEK, and persisted in `projects.encryptedDek`. From then on every variable on that project is encrypted with that DEK. The sealing primitive underneath is [`iron-webcrypto`](https://github.com/brc-dd/iron-webcrypto){rel=""nofollow""}, configured with authenticated encryption (`aes-256-gcm`). A tampered ciphertext fails to decrypt — there is no silent downgrade. ## Why envelope encryption? ::card-group :::card --- icon: i-lucide-zap --- #title Fast key rotation #description Rotating a project's DEK only re-encrypts that project's variables. A platform-wide rotation changes only the KEK; every sealed DEK is re-sealed once and business continues. ::: :::card --- icon: i-lucide-shield --- #title Scoped blast radius #description A leaked DEK compromises one project, not every secret on the instance. The KEK never leaves the server. ::: :::card --- icon: i-lucide-layers --- #title BYOK-ready #description The DEK layer is the seam where hardware-backed key storage (KMS, HSM, passkeys) will plug in without touching application code. ::: :: ## Backward compatibility Projects created before the envelope upgrade have no `encryptedDek` column value. Their variables keep decrypting directly with the KEK and the first new write provisions a DEK automatically. Reads tolerate mixed-state data: the service tries the project DEK first, then falls back to the KEK so no variable is left unreadable during the transition. ## What is stored in the database | Column | What it holds | | -------------------------- | ----------------------------------------------------------------- | | `variables.encryptedValue` | Sealed ciphertext of the secret value. Never plaintext. | | `projects.encryptedDek` | Project DEK, sealed by the KEK. `null` for pre-envelope projects. | | `tokens.hash` | `sha256(token)` as hex. The plaintext token is never stored. | | `tokens.prefix` | Non-secret 12-char prefix used for display and audit logs. | ## API tokens API tokens follow a different (but compatible) model: they are hashed, not encrypted. See [API Tokens](https://shelve.cloud/docs/core-features/tokens) for the full story. ## In transit Traffic to `app.shelve.cloud` uses TLS 1.3 end-to-end. The CLI pins the same endpoint and refuses downgrade. For self-hosted instances, configure HTTPS at your reverse proxy or platform. ## Self-host checklist 1. Generate a strong KEK: `openssl rand -base64 48`. 2. Set it as `NUXT_PRIVATE_ENCRYPTION_KEY` on your platform. 3. **Never** rotate the KEK in-place without re-sealing existing DEKs — existing data becomes unreadable. A safe rotation procedure is on the [roadmap](https://github.com/HugoRCD/shelve/issues){rel=""nofollow""}. 4. Back up `projects.encryptedDek` alongside `variables.encryptedValue`; losing either makes the corresponding data unrecoverable. # Environments ![environments](https://shelve.cloud/docs/environments.png) Environments are a way to group variables by context. You can create environments for different stages of your application like development, staging, production for example. Each environment can have its own set of variables, which allows you to manage your configurations more efficiently. You can create as many environments as you want. To create an environment, click on the `New Environment` button, give it a name (e.g., `preview`, `feat/218`, `staging`, `production`), and click on the `Create` button. The name must be unique across all environments. Creating an environment will automatically add a field to set a variable value for this environment. ::caution You can delete an environment but be careful, this action is irreversible and will delete all variables associated with this environment. :: # Projects ![projects](https://shelve.cloud/docs/project.png) Projects are the core of Shelve, a project is a collection of environment variables and secrets, but it can also contain other resources like quick links, files (soon), and more. ### Variables Variables are key-value pairs that store environment configurations and secrets. You can create variables for different environments like development, staging, production, or even custom environments. Check the [Variables](https://shelve.cloud/docs/core-features/variables) page for more information. ### Quick Links Quick links are shortcuts to external resources like documentation, repositories, or any other URL. You can create quick links for your project to easily access external resources without leaving Shelve in the settings tab. ### Files (soon) Files will be a new feature that allows you to store files in your project. You can upload files like certificates, images, or any other file type to your project. Files will enable you to store all your project resources in one place. ### Repository The repository serves as a link to your project's repository. You can connect your project to a GitHub repository to synchronize your secrets with GitHub secrets. By linking your project to a repository, you enhance Shelve's capabilities, enabling you to automatically synchronize your secrets with GitHub secrets, which simplifies the management of your secrets across your projects. But don't worry, your secrets are always encrypted and secure. You will benefit from a lot more features in the future ! # Teams ![teams](https://shelve.cloud/docs/teams.png) Shelve is designed to be used by teams of developers but can also be used by solo developers. A team is a group of users who work together on a project. Teams can have multiple members, each with different roles that determine their permissions within the team. ### Roles Shelve has three roles: **Owner**, **Admin**, and **Member**. - **Owner**: The user who created the team. The owner has full control over the team, members and projects. He's the only one who can delete projects, variables, environments, and the team itself. - **Admin**: Users with admin permissions can manage the team, members, and projects. They can't delete the team or change the owner. - **Member**: Users with member permissions can only view the settings of the team and projects. They can't make any changes but can create and update variables. ### Workspaces Team act as a container for projects and variables. Each team has its own workspace where you can create and manage projects and variables. Team workspaces are isolated from each other, meaning that projects and variables from one team are not accessible by another team. ### Slug A team slug is a unique identifier for a team. It is used in URLs to access the team's workspace. The team slug is automatically generated when you create a team and can be customized later but must be unique across all teams. This also the unique identifier that you can use to access the team's workspace via the CLI ```json [shelve.json] { "slug": "nuxtlabs", "project": "@nuxt/ui" } ``` ## Create a team Use the team selector and type the team name in the search field to create a new team. :video{autoPlay="true" loop="true" muted="true" src="https://shelve.cloud/docs/create-team.mp4"} # API Tokens API tokens let the CLI and any external system authenticate against Shelve. They are managed from your account settings at [app.shelve.cloud/user/tokens](https://app.shelve.cloud/user/tokens){rel=""nofollow""}. ## How tokens are stored - The plaintext value is **shown exactly once**, at creation. Copy it immediately — Shelve never stores it and cannot retrieve it later. - Only the SHA-256 hash of the token is written to the database, alongside a non-secret prefix (`she_…` — the first 12 characters). The prefix is what you see in the token list and in audit logs; it is useful for identifying a token without revealing it. - Lookups run in constant time (`timingSafeEqual` on the hash), so there is no side channel that leaks bit-by-bit comparisons. ::callout{type="info"} Tokens are produced from `crypto.randomBytes(32)` and encoded in [Crockford base32](https://www.crockford.com/base32.html){rel=""nofollow""} . They are 256 bits of entropy and cannot be brute-forced. :: ## Scoped tokens By default a token inherits your account's full access. You can narrow it when creating the token: ::field-group :::field{name="permissions" type="('read' | 'write')[]"} At least one is required. `read` grants listing and fetching, `write` grants mutations. A read-only token that tries to POST a variable receives `403 Token missing 'write' permission` . ::: :::field{name="teamIds" type="number[]"} Restrict the token to one or more teams. Requests targeting any other team return `403 Token not authorized for this team` . ::: :::field{name="projectIds" type="number[]"} Restrict the token to specific projects within the allowed teams. ::: :::field{name="environmentIds" type="number[]"} Restrict the token to specific environments. Useful for CI tokens that should only read `production` secrets, for example. ::: :: Scope enforcement happens server-side in a dedicated middleware; the token can never outgrow the scope it was issued with. ## Expiry Every token may carry an `expiresAt`. After that date the token is refused with `401 Token expired` even before it is hashed and matched. Rotating a short-lived CI token is as simple as creating a new one; no restart or config push is required. ## IP allowlist Optionally attach a list of CIDR blocks to a token. Requests originating outside those blocks are rejected with `401 Token not authorized for this network`. Both IPv4 and IPv6 notations are supported. ## Audit trail Token lifecycle events (`token.create`, `token.delete`) and every authenticated request surface in [audit logs](https://shelve.cloud/docs/core-features/audit-logs) with the token prefix, actor, IP, and user agent. When something looks wrong, revoke the token from the UI — the hash is deleted and the next request using it returns `401`. ## Sending tokens The CLI and any integration should send the token as an `Authorization: Bearer ` header. The legacy `Cookie: authToken=…` path still works for older CLI builds but returns `Deprecation: true` / `Sunset: Wed, 01 Jul 2026 00:00:00 GMT` response headers — migrate clients before that date. # Variables Variables are key-value pairs that store environment configurations and secrets. You can create variables for different environments like development, staging, production or even custom environments. Understanding how to effectively manage these variables is crucial for maintaining a secure and efficient development workflow. ## Creating Variables ### Variables Form The variables form is an incredibly powerful tool that allows you to create variables for multiple environments at the same time. You can easily import variables from a file or export them by clicking on the three dots in the top right corner. Plus, you can simply drag and drop a `.env` file (or any other supported files containing variables) or copy/paste to import variables, which will be automatically parsed and cleaned by removing comments, empty lines, and quotes. Working with variable names is made easier through several thoughtful features. As you type, the form automatically converts your keys to uppercase, following the common convention for environment variables. You'll also find a prefix button that helps you maintain consistency with framework-specific prefixes like `NUXT_PRIVATE_` or `REACT_APP_`. For generating secure values, especially useful for passwords or tokens, the Value Generator button provides random strings that meet your security requirements. ### Variable Item ![variable-item](https://shelve.cloud/docs/variable-item.png) Each variable in Shelve is designed to provide clear visibility and easy access to its information. When looking at a variable, you'll see its key, value, and environment status all clearly displayed. Values are masked by default to protect sensitive information, but you can easily reveal them using the `eye` icon when needed. The copy functionality goes beyond simple value copying. While you can copy individual fields, you can also copy the entire variable string in the format `KEY=value` by clicking on the environment name. This becomes particularly useful when you need to quickly share or transfer variables between projects. One of the most valuable features is the environment indicator system. These indicators provide an immediate visual understanding of where each variable is set. When managing large projects with multiple environments, this becomes invaluable for ensuring consistency and identifying potential missing configurations. ## Variables Selector :video{autoPlay="true" loop="true" muted="true" src="https://shelve.cloud/docs/variable-selector.mp4"} You can easily select multiple variables by clicking on each item. Once selected, you can perform bulk actions like copying, deleting, or sending them to Github Secrets if you've connected your project to a repository and install a [Github App](https://shelve.cloud/docs/integrations/github) to synchronize your secrets with Github Secrets. # Init The `init` command prepares a repository for use with Shelve and, crucially, keeps your `.env*` files out of the context of AI coding agents. Running it is a one-off setup step — run it once per project. ```bash [terminal] shelve init ``` ## What it does - Creates (or updates) an ignore file for every major AI agent so they can never read cached secrets: - `.cursorignore` - `.aiderignore` - `.codeiumignore` - `.continueignore` - `.aigignore` - Appends a managed block to your `.gitignore` if one is not already present, so the encrypted cache directory (`.shelve/`) and all `.env*` files stay out of version control. `.env.example` and `.env.template` are whitelisted so you can still commit references. Each ignore file is framed with `# shelve-managed-block` / `# end shelve-managed-block` markers. Running `shelve init` again only rewrites the managed block — your own entries above and below are preserved. ## Options ::field-group :::field{name="cwd" type="string"} Directory to initialize. Defaults to the current working directory. ::: :: ## Why this matters AI coding agents (Cursor, Claude Code, Codex, Aider, Continue…) can read every file in your workspace unless you tell them otherwise. If you ever run `shelve pull` to write a `.env` to disk, an agent can trivially exfiltrate its contents into a prompt or a suggested commit. `shelve init` closes that hole in one command. ::callout{type="info"} Even with agent ignore files in place, prefer `shelve run -- ` : it pipes secrets to your subprocess through the environment and never writes them to disk in the first place. :: # CLI The CLI serves as a command-line interface designed for the [Shelve app](https://dub.sh/shelve){rel=""nofollow""}. Facilitating the seamless transfer of environment variables for project collaboration within a team directly through the terminal interface, but way more other features are and will be available ! ## Installation To install the CLI, please refer to the [Quickstart](https://shelve.cloud/docs/getting-started/quickstart) guide. ## Configuration Configuration is loaded from cwd. You can use either shelve.json, shelve.config.json or .shelverc.json, but running the CLI without any configuration will create a shelve.json file. The CLI also has a json schema for the configuration file. that can be used to validate the configuration file [(see it here)](https://raw.githubusercontent.com/HugoRCD/shelve/main/packages/types/schema.json){rel=""nofollow""}. ```json [shelve.json] { "slug": "nuxtlabs", "project": "@nuxt/ui", "confirmChanges": true, "autoCreateProject": true } ``` ### Monorepo support Shelve natively supports monorepos, tf you are using a monorepo, Shelve will automatically detect the root of the monorepo and look for the global `shelve.json` file. You can define here common configurations for all the projects in the monorepo (the team `slug` for example): ::code-tree{default-value="shelve.json"} ```json [apps/app/shelve.json] { "project": "@nuxt/app", "confirmChanges": true } ``` ```json [apps/app/package.json] { "name": "@nuxt/app" } ``` ```json [apps/api/shelve.json] { "project": "@nuxt/api", "envFileName": ".env.development" } ``` ```json [apps/api/package.json] { "name": "@nuxt/api" } ``` ```json [packages/cli/shelve.json] { "project": "@nuxt/cli" } ``` ```json [packages/cli/package.json] { "name": "@nuxt/cli" } ``` ```json [shelve.json] { "slug": "nuxtlabs" } ``` ```json [package.json] { "name": "nuxtlabs" } ``` :: ### Monorepo usage If you are using a monorepo, running a command at the root level will execute the command for all the projects in the monorepo that have a `shelve.json` file. Example: ```bash [terminal] shelve pull ``` This command will execute the `pull` command for all the projects in the monorepo that have a `shelve.json` file. ```bash [apps/app/terminal] shelve pull ``` This command will execute the `pull` command for the `@nuxt/app` project. ## Options Here are all the available options for the configuration file: | Option | Type | Default | Description | | ------------------- | --------- | --------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- | | `project` | `string` | `process.env.SHELVE_PROJECT` or nearest package.json name | The name of your project | | `slug` | `string` | `process.env.SHELVE_TEAM_SLUG` | Your team slug (can be found in your team settings) | | `token` | `string` | OS keychain (preferred) or `$XDG_CONFIG_HOME/.shelve` after `shelve login`, or `process.env.SHELVE_TOKEN` | Authentication token created via app.shelve.cloud/user/tokens | | `url` | `string` | `https://app.shelve.cloud` | URL of the Shelve instance | | `defaultEnv` | `string` | | The default environment use by the command like `run`, `push` or `pull` | | `confirmChanges` | `boolean` | `false` | Whether to confirm changes before applying them | | `envFileName` | `string` | `.env` | Name of your environment file | | `autoUppercase` | `boolean` | `true` | Automatically uppercase environment variable keys | | `autoCreateProject` | `boolean` | `true` | Automatically create project if it doesn't exist | ## Environment Variables The following environment variables can be used to override configuration options: | Variable | Description | | -------------------- | --------------------------- | | `SHELVE_PROJECT` | Project name | | `SHELVE_TEAM_SLUG` | Team slug | | `SHELVE_TOKEN` | Authentication token | | `SHELVE_URL` | Shelve instance URL | | `SHELVE_DEFAULT_ENV` | Default env for CLI command | # Run `shelve run` spawns your command with every variable from your Shelve project already present in its environment. No `.env` file is written, no secret ever touches disk. ```bash [terminal] shelve run -- [args] ``` The double-dash is conventional — anything after `--` is passed verbatim to your command. For simple npm-style scripts, `shelve run dev` works too: it uses [`ni`](https://github.com/antfu-collective/ni){rel=""nofollow""} to detect your package manager and run the script. ## How it works 1. Shelve reads your token (from the OS keychain or the XDG config file), resolves your team / project / environment, and fetches the variables for that env. 2. The variables are spliced into a fresh environment map and merged onto your process's environment. 3. Your command is spawned via `node:child_process.spawn`, in its own process group, so Shelve can forward `SIGINT` / `SIGTERM` cleanly. 4. The exit code of your command becomes the exit code of `shelve run` — seamless in CI. ## Examples ```bash [terminal] # Run your dev server with production-like secrets shelve run --env=preview -- pnpm dev # Short form when the command is a package.json script shelve run build # Explicit command + flags shelve run -- node scripts/migrate.ts --dry-run ``` ## Options ::field-group :::field{name="env" type="string"} Environment to use (for example `development` , `preview` , `production` ). Overrides `defaultEnv` from `shelve.json` . ::: :::field{name="watch" type="boolean"} Restart the child process whenever variables change upstream. Combine with a dev server that already reloads on source edits. ::: :::field{default="'auto'" name="offline" type="'auto' | 'never' | 'only'"} How to use the encrypted offline cache. `auto` (default) refreshes variables from Shelve and falls back to the cache if the network is down. `never` disables the cache entirely. `only` refuses to make any network call. ::: :::field{default="15m" name="cache-ttl" type="duration"} How long a cached payload is considered fresh. Accepts `500ms` , `30s` , `15m` , `2h` , `7d` , or a raw number in milliseconds. ::: :: ## Secret references (`.env.template`) If a `.env.template` file is present in the current directory, Shelve uses it as a **layout** for the injected environment. Each line is either a literal value or a `shelve://` reference that resolves against the current environment: ```bash [.env.template] NODE_ENV=production DATABASE_URL=shelve://DATABASE_URL PROD_DATABASE_URL=shelve://production/DATABASE_URL ``` - Lines without `shelve://` pass through verbatim. - `shelve://KEY` pulls `KEY` from the current environment, optionally renaming it on the left-hand side. - `shelve:///KEY` explicitly targets another environment — useful for mirroring production values into a preview command. References that cannot be resolved are reported in a grouped diagnostic and the command aborts before spawning the child, so you never run a process with partially-hydrated secrets. ## Encrypted offline cache After every successful fetch, Shelve writes the payload to `~/.shelve/cache/.cache`, sealed with a key derived from your current token via HKDF. The cache is scoped per team + project + environment; switching environments uses a different key. - Loss of the token invalidates the cache automatically (the derived key no longer matches). - Tampering with the cache file is detected via AES-GCM authentication tags — a modified file fails to decrypt. - Files are created with `0600` permissions; the directory with `0700`. Use `--offline=only` on planes, in restricted CI environments, or anywhere outbound egress is blocked. ## Watch mode ```bash [terminal] shelve run --watch -- node server.js ``` With `--watch`, Shelve opens a server-sent events stream and listens for `variable.updated` / `variable.deleted` events on your project. When a change lands the child process is sent `SIGTERM`, waits for it to exit (or is killed after a grace period), and is respawned with the new environment. This is the closest thing to a managed "hot reload for secrets". ## AI-agent safety When `shelve run` is invoked from a shell whose environment looks like an AI coding agent (Cursor, Claude Code, Aider, Continue, Codex, …), it prints a reminder that secrets will only exist in the spawned process's memory and will never be visible to the agent itself. Unlike `shelve pull`, `run` never prompts — running with an agent attached is safe by design. ## Exit behaviour | Signal / event | Behaviour | | ---------------------------- | --------------------------------------------------------------- | | Child exits normally | `shelve run` exits with the same code | | Child crashes | Shelve propagates the exit code | | `Ctrl-C` (`SIGINT`) | Forwarded to the entire process group; child terminates cleanly | | `SIGTERM` | Forwarded to the process group, grace period 5s, then `SIGKILL` | | Variable change (watch mode) | Child is respawned with the new env | # Login and Logout ## Login ```bash [terminal] shelve login ``` The CLI prompts for a token. Create one from the Shelve dashboard at [app.shelve.cloud/user/tokens](https://app.shelve.cloud/user/tokens){rel=""nofollow""}, then paste it into the prompt. Tokens are [scopeable, expiring, and IP-bound](https://shelve.cloud/docs/core-features/tokens) — prefer a narrowly-scoped token to your account-wide default. ### Where the token is stored 1. **OS keychain first** — Keychain on macOS, Credential Vault on Windows, Secret Service / GNOME Keyring / KWallet on Linux (via [`@napi-rs/keyring`](https://github.com/napi-rs/keyring){rel=""nofollow""}). The value is encrypted at rest and gated by the OS session. 2. **XDG file fallback** — if the keychain is unavailable (headless CI containers, missing D-Bus on Linux, …) the CLI writes to `$XDG_CONFIG_HOME/.shelve` (typically `~/.config/.shelve`) with mode `0600`. A warning is printed so you know you are on the fallback path. ::callout{type="info"} Older CLI versions wrote credentials to `~/.shelve` . The first `login` / `logout` / `pull` you run with a v5 CLI will migrate the legacy file into the XDG location and delete the original. :: ### Me ```bash [terminal] shelve me ``` Prints the account the current token is attached to, the Shelve instance URL, and which storage backend holds the token. ## Logout ```bash [terminal] shelve logout ``` Removes the token from the keychain (if present) and clears the XDG config. Subsequent CLI commands refuse to run until you log in again. ## Non-interactive environments In CI, a container, or any automation flow, skip `shelve login` entirely and pass the token via an environment variable: ```bash [terminal] SHELVE_TOKEN="$SHELVE_TOKEN" shelve run -- pnpm test ``` The CLI reads `SHELVE_TOKEN` before consulting the keychain or the XDG file. Pair it with a scoped, expiring token for least-privilege CI access. ::callout{type="warning"} Never commit `SHELVE_TOKEN` to git. Use your CI provider's secrets UI or a runtime secret store (GitHub Actions secrets, Vercel project env vars, …). :: ## Multiple accounts Only one account per machine is supported at a time. To switch accounts, `shelve logout` and `shelve login` again with the new token. # Push and Pull The `push` and `pull` commands enable you to send your secrets to Shelve and retrieve them back to your local machine. This is the essential feature of Shelve CLI, as it lets you securely store your secrets in Shelve and access them from your local machine, while also allowing you to share them with your team. ## Push The `push` command allows you to send your secrets to Shelve. When you run the `push` command, you will be prompted to enter the environment name where you want to store your secrets. ```bash [terminal] shelve push ``` ## Pull The `pull` command allows you to retrieve your secrets from Shelve. When you run the `pull` command, you will be prompted to enter the environment name from which you want to retrieve your secrets. ```bash [terminal] shelve pull ``` ### Options ::field-group :::field{name="env" type="string"} Specify the environment to which you want to push or pull the variables ::: :: You can configure a default environment in your `shelve.json` file to bypass the env prompt: ```json [shelve.json] { "defaultEnv": "development", "slug": "shelve" } ``` # Create The `create` command enables you to initiate a new project in Shelve. When you execute the `create` command, if there is no configuration file located in the current directory, you will be asked to provide the project name (which will be automatically filled with the current package name specified in the `package.json` file), along with the project description, repository URL, and homepage URL. ```bash [terminal] shelve create ``` ### Examples ```bash [terminal] shelve create --name my-project --slug my-team ``` ### Options ::field-group :::field{name="name" type="string"} The name of the project you want to create ::: :::field{name="slug" type="string"} The slug of the team to which you want the project to belong ::: :: # Config The `config` command allows you to view the current configuration of Shelve CLI. When you run the `config` command, Shelve CLI will display the current configuration, including the token, the base URL, and everything else that will be used to interact with Shelve. ```bash [terminal] shelve config ``` Example output: ```bash [terminal] { slug: 'hrcd', project: 'shelve', token: 'your-token', url: 'https://app.shelve.cloud', username: 'your-username', email: 'your-email', confirmChanges: false, envFileName: '.env', autoUppercase: true, autoCreateProject: true, monorepo: { paths: [ '/Users/username/Dev/shelve', '/Users/username/Dev/shelve/apps/base', '/Users/username/Dev/shelve/apps/lp', '/Users/username/Dev/shelve/apps/shelve', '/Users/username/Dev/shelve/packages/cli', '/Users/username/Dev/shelve/packages/utils', '/Users/username/Dev/shelve/packages/types' ] }, workspaceDir: '/Users/username/Dev/shelve', isMonoRepo: true, isRoot: false, '$schema': 'https://raw.githubusercontent.com/HugoRCD/shelve/main/packages/types/schema.json' } ``` # Generate The `generate` will prompt you to select the type of file you want to generate. ```bash [terminal] shelve generate ``` ## Env Example The `.env.example` option allows you to generate an example `.env` file in your project. This file contains the environment variables required to run your project locally but does not contain any sensitive information. You can use this file as a template to create your own `.env` file. # Upgrade The `upgrade` command allows you to upgrade your Shelve CLI to the latest version. When you run the `upgrade` command, Shelve CLI will check for the latest version and upgrade your CLI if a newer version is available. ```bash [terminal] shelve upgrade ``` # Github ![github](https://shelve.cloud/docs/github-apps.png) Shelve integrates with GitHub to provide a seamless experience for developers. By linking your project to a repository, you enhance Shelve's capabilities, enabling you to synchronize your secrets with GitHub secrets, which simplifies the management of your secrets across your projects. But don't worry, your secrets are always encrypted and secure. ## GitHub Apps GitHub Apps are the recommended way to integrate with GitHub because they offer more granular permissions and better security. Shelve uses GitHub Apps to provide a secure and seamless integration with GitHub. You can create a GitHub app in the integrations tab in the `User` section. After creating the GitHub app, you will be redirected to the GitHub app page where you can manage the app, install it on your repositories, set the permissions, and more. ## Secrets Send your secrets to GitHub to synchronize them with GitHub secrets. By sending your secrets to GitHub, you can use them in your CI/CD pipelines, GitHub Actions, and more. You can select the variables you want to send to GitHub and choose the repositories you want to send them to. You will first need to create a GitHub app, install it on your repositories, and link your project to a repository to send your secrets to GitHub. ::steps ### Select the variables ![variables-selector](https://shelve.cloud/docs/variables-selector.png) ### Send to GitHub ![send-to-github](https://shelve.cloud/docs/send-to-github.png) :: ::note{icon="i-lucide-rocket"} Is it planned in the future to enhance even more the integration with GitHub, stay tuned for more features! :: # Deploy on Vercel Vercel is the official recommended provider for self-hosting Shelve. This platform offers native integration with the ecosystem that Shelve uses and greatly simplifies deployment. ## Prerequisites - A [Vercel](https://vercel.com){rel=""nofollow""} account - A GitHub repository with Shelve source code - A PostgreSQL database (we recommend [Neon](https://neon.tech){rel=""nofollow""}) ## Recommended Vercel native integrations For an optimal experience, we recommend using Vercel's native integrations: - **Database**: [Neon PostgreSQL](https://vercel.com/integrations/neon){rel=""nofollow""} - **Cache/Session**: [Vercel KV (Redis)](https://vercel.com/storage/kv){rel=""nofollow""} - **Email**: [Resend](https://vercel.com/integrations/resend){rel=""nofollow""} - **AI**: [Vercel AI SDK](https://vercel.com/ai){rel=""nofollow""} if you use AI features These integrations are optimized for Vercel and offer simplified configuration. ## Quick deployment ### 1. Fork and deployment 1. Fork the Shelve repository to your GitHub account 2. Connect your repository to Vercel 3. Vercel will automatically detect that it's a Nuxt application ### 2. Environment variables configuration Before the first deployment, you need to configure environment variables. Go to your Vercel project settings > Environment Variables. ## Required environment variables These variables are **mandatory** for Shelve to work: ```bash # PostgreSQL database DATABASE_URL=postgresql://username:password@host:port/database # Security (generate random keys of 32+ characters) NUXT_SESSION_PASSWORD=your-32-character-minimum-session-password NUXT_PRIVATE_ENCRYPTION_KEY=your-32-character-minimum-encryption-key ``` ### Security keys generation You can generate secure keys with this command: ```bash # Generates a 64-character key openssl rand -base64 48 ``` ## Database configuration Shelve requires a PostgreSQL database. We recommend [Neon](https://neon.tech){rel=""nofollow""} for its excellent Vercel integration, but you can use any PostgreSQL provider. ### Option 1: Using Vercel-Neon integration (Recommended) 1. Use the [Vercel-Neon integration](https://vercel.com/integrations/neon){rel=""nofollow""} directly 2. The integration will automatically configure the `DATABASE_URL` environment variable 3. No manual configuration needed ### Option 2: Manual configuration 1. Create a PostgreSQL database with any provider ([Neon](https://neon.tech){rel=""nofollow""}, [Supabase](https://supabase.com){rel=""nofollow""}, [Railway](https://railway.app){rel=""nofollow""}, etc.) 2. Copy the PostgreSQL connection URL 3. Add it as `DATABASE_URL` variable in Vercel project settings ## Authentication configuration Shelve supports multiple authentication methods. **You need at least one authentication method** configured for users to create accounts and log in. ### Option 1: Email authentication (Recommended) Configure email service variables below to enable OTP-based authentication via email. ### Option 2: OAuth authentication Configure OAuth providers below for social authentication. ### GitHub OAuth To enable GitHub authentication: ```bash # GitHub OAuth (both variables are required together) NUXT_OAUTH_GITHUB_CLIENT_ID=your_github_client_id NUXT_OAUTH_GITHUB_CLIENT_SECRET=your_github_client_secret ``` ### Google OAuth To enable Google authentication: ```bash # Google OAuth (both variables are required together) NUXT_OAUTH_GOOGLE_CLIENT_ID=your_google_client_id NUXT_OAUTH_GOOGLE_CLIENT_SECRET=your_google_client_secret ``` ## Email service (Required for email authentication) For email-based authentication using OTP codes: ```bash # Resend API key (required for email authentication) NUXT_PRIVATE_RESEND_API_KEY=re_your_resend_api_key NUXT_PRIVATE_SENDER_EMAIL=noreply@yourapp.com ``` ::callout{type="warning"} **Important** : These variables are now required if you want to enable email authentication. Without them, users can only authenticate via OAuth providers. :: ## Optional environment variables ### Administration and security ```bash # Admin emails (comma-separated) NUXT_PRIVATE_ADMIN_EMAILS=admin@yourapp.com,admin2@yourapp.com # Allowed origins for CORS (comma-separated) NUXT_PRIVATE_ALLOWED_ORIGINS=https://yourapp.com,https://www.yourapp.com ``` ## Advanced GitHub integration To use GitHub integration (secrets synchronization), you need to configure a GitHub App: ### 1. Create a GitHub App 1. Go to your GitHub organization settings 2. Create a new GitHub App with the following permissions: **Repository permissions:** - Actions: Read - Administration: Read - Contents: Read - Metadata: Read - Secrets: Read and write - Variables: Read and write - Webhooks: Read and write **Organization permissions:** - Secrets: Read and write - Variables: Read and write ### 2. Configure private key ```bash # GitHub private key (PKCS#8 base64 format) NUXT_PRIVATE_GITHUB_PRIVATE_KEY=your_base64_encoded_private_key ``` ::callout{type="warning"} The GitHub private key must be converted from PKCS#1 to PKCS#8 format, then base64 encoded. Use this command: ```bash openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in private-key.pem | base64 -w 0 ``` :: ## Automatic validation Shelve automatically validates environment variables configuration at startup. If a required variable is missing or invalid, the application will display a detailed error. Vercel deployment logs will show you: - ✅ GitHub OAuth: enabled/disabled - ✅ Google OAuth: enabled/disabled - ✅ Email Service: enabled/disabled ## Deployment Once all variables are configured: 1. Commit and push your changes (if necessary) 2. Vercel will automatically deploy your application 3. Your Shelve instance will be accessible at the URL provided by Vercel ## Custom domain configuration 1. In your Vercel project settings, add your domain 2. Configure DNS according to Vercel instructions 3. Update `NUXT_PRIVATE_ALLOWED_ORIGINS` with your new domain ## Monitoring and logs Use Vercel tools to monitor your deployment: - **Functions**: API routes performance - **Analytics**: Traffic and performance - **Logs**: Real-time debugging # Environment variables This page documents all environment variables available in Shelve, their usage and configuration. ## Required variables These environment variables are **mandatory** for Shelve to function: ### `DATABASE_URL` - **Type**: String (PostgreSQL URL) - **Required**: ✅ Yes - **Description**: Connection URL to your PostgreSQL database - **Format**: `postgresql://username:password@host:port/database` - **Example**: `postgresql://user:pass@db.example.com:5432/shelve` ::callout{type="info"} **Recommendation** : Use [Neon](https://neon.tech){rel=""nofollow""} with [Vercel integration](https://vercel.com/integrations/neon){rel=""nofollow""} for simplified configuration. :: ### `NUXT_SESSION_PASSWORD` - **Type**: String - **Required**: ✅ Yes - **Constraint**: Minimum 32 characters - **Description**: Secret key to sign and encrypt user sessions - **Generation**: `openssl rand -base64 48` ### `NUXT_PRIVATE_ENCRYPTION_KEY` - **Type**: String - **Required**: ✅ Yes - **Constraint**: Minimum 32 characters - **Description**: Key Encryption Key (KEK). Used to seal the per-project Data Encryption Keys (DEKs) that actually protect variable values. See [Encryption](https://shelve.cloud/docs/core-features/encryption) for the full model. - **Generation**: `openssl rand -base64 48` ::callout{type="warning"} **Important** : Never modify these keys after first deployment, it would make existing data inaccessible. A safe KEK rotation flow (re-sealing all DEKs) is on the roadmap. :: ## Authentication configuration Shelve now supports multiple authentication methods. **You need to configure at least one method** for users to be able to create accounts and log in: ### Option 1: Email authentication only (Recommended) - Configure `NUXT_PRIVATE_RESEND_API_KEY` and `NUXT_PRIVATE_SENDER_EMAIL` - Users can sign up and log in using their email with OTP codes - No OAuth setup required ### Option 2: OAuth only - Configure at least one OAuth provider (GitHub or Google) - Users authenticate via OAuth providers - No email service required ### Option 3: Both methods (Maximum flexibility) - Configure both email service and OAuth providers - Users can choose their preferred authentication method - Recommended for production environments ## Authentication variables Shelve supports multiple authentication methods. You need **at least one authentication method** configured: ### Email authentication (OTP) For email-based authentication using one-time passwords (OTP), you need to configure email service variables below. ### OAuth authentication (Optional) OAuth providers offer an alternative authentication method alongside email authentication. ### GitHub OAuth To enable GitHub authentication, configure these two variables together: #### `NUXT_OAUTH_GITHUB_CLIENT_ID` - **Type**: String - **Required**: 🔶 Optional (must be paired with CLIENT\_SECRET) - **Description**: Client ID of your GitHub OAuth App - **How to get**: [GitHub Developer Settings](https://github.com/settings/developers){rel=""nofollow""} #### `NUXT_OAUTH_GITHUB_CLIENT_SECRET` - **Type**: String - **Required**: 🔶 Optional (must be paired with CLIENT\_ID) - **Description**: Client Secret of your GitHub OAuth App - **How to get**: [GitHub Developer Settings](https://github.com/settings/developers){rel=""nofollow""} ### Google OAuth To enable Google authentication, configure these two variables together: #### `NUXT_OAUTH_GOOGLE_CLIENT_ID` - **Type**: String - **Required**: 🔶 Optional (must be paired with CLIENT\_SECRET) - **Description**: Client ID of your Google OAuth App - **How to get**: [Google Cloud Console](https://console.cloud.google.com/){rel=""nofollow""} #### `NUXT_OAUTH_GOOGLE_CLIENT_SECRET` - **Type**: String - **Required**: 🔶 Optional (must be paired with CLIENT\_ID) - **Description**: Client Secret of your Google OAuth App - **How to get**: [Google Cloud Console](https://console.cloud.google.com/){rel=""nofollow""} ## GitHub integration variables ### `NUXT_PRIVATE_GITHUB_PRIVATE_KEY` - **Type**: String (Base64) - **Required**: 🔶 Optional - **Description**: Private key of your GitHub App for advanced integration - **Usage**: Automatic secrets synchronization with GitHub - **Format**: PKCS#8 encoded in base64 ::callout{type="info"} **Required setup** : This variable requires a GitHub App configured with comprehensive permissions including Secrets, Variables, and more. See the [Vercel documentation](https://shelve.cloud/docs/self-hosting/vercel#advanced-github-integration) for the complete permissions list. :: #### How to get this key 1. Create a GitHub App in your organization 2. Download the private key (.pem format) 3. Convert it to PKCS#8 format and encode in base64: ```bash openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in private-key.pem | base64 -w 0 ``` ## Email service variables ### `NUXT_PRIVATE_RESEND_API_KEY` - **Type**: String - **Required**: ⚠️ Required for email authentication (OTP) - **Format**: Must start with `re_` - **Description**: Resend API key for sending emails including OTP codes - **Usage**: OTP authentication, welcome emails, notifications - **How to get**: [Resend Dashboard](https://resend.com/dashboard){rel=""nofollow""} ::callout{type="warning"} **Important** : This variable is now required if you want to use email authentication (OTP). Without it, users can only authenticate via OAuth providers. :: ::callout{type="info"} **Recommendation** : Use [Resend](https://resend.com){rel=""nofollow""} with [Vercel integration](https://vercel.com/integrations/resend){rel=""nofollow""} for automated configuration. :: ### `NUXT_PRIVATE_SENDER_EMAIL` - **Type**: String (Email) - **Required**: ⚠️ Required for email authentication (required if RESEND\_API\_KEY is set) - **Description**: Sender email address for OTP codes and notifications - **Format**: `noreply@yourdomain.com` - **Constraint**: Must be a valid email verified in Resend ## Administration variables ### `NUXT_PRIVATE_ADMIN_EMAILS` - **Type**: String (comma-separated list) - **Required**: 🔶 Optional - **Description**: List of emails with access to admin features - **Format**: `admin@app.com,admin2@app.com` - **Usage**: Access to statistics, user management ### `NUXT_PRIVATE_ALLOWED_ORIGINS` - **Type**: String (comma-separated list of URLs) - **Required**: 🔶 Optional - **Description**: Allowed origins for CORS requests - **Format**: `https://app.com,https://www.app.com` - **Usage**: Security, CSRF attack prevention ## Automatic validation Shelve uses an automatic environment variables validation system: - **Startup validation**: The application checks all required variables - **Format validation**: URLs, emails, minimum lengths are verified - **Consistency validation**: OAuth pairs are validated together - **Explicit error messages**: In case of error, logs indicate exactly what's wrong ## Configuration by environment ### Local development Create a `.env` file at the root of your project: ```bash # Copy from .env.example DATABASE_URL=postgresql://localhost:5432/shelve_dev NUXT_SESSION_PASSWORD=your-dev-session-password-32-chars-min NUXT_PRIVATE_ENCRYPTION_KEY=your-dev-encryption-key-32-chars-min ``` ### Production on Vercel Configure all variables in your Vercel project settings: 1. Vercel Project > Settings > Environment Variables 2. Add each variable with its value 3. Select appropriate environments (Production, Preview, Development) ## Troubleshooting ### Common errors **"Session password must be at least 32 characters long"** - Generate a longer key: `openssl rand -base64 48` **"Must be a valid PostgreSQL URL"** - Check the format: `postgresql://user:pass@host:port/db` - Test the connection to your database **"GitHub OAuth validation failed"** - Make sure you have defined CLIENT\_ID AND CLIENT\_SECRET together - Verify that the values are correct **"Resend API key must start with 're\_'"** - Use the Resend API key, not a test key - Verify that you copied the complete key ### Missing environment variables Vercel deployment logs will show: ```text 🔐 Auth configuration validated: GitHub OAuth: ✅/❌ Google OAuth: ✅/❌ Email Service: ✅/❌ ``` This allows you to quickly check which integrations are active. # Introduction Thank you for your interest in contributing to Shelve! We welcome contributions from the community and are excited to work with you. Please follow the guidelines below to ensure a smooth and effective contribution process. ## Contribution Guidelines ### Reporting Issues If you encounter any issues or bugs while using Shelve, please report them by opening an issue on our [GitHub Issues](https://github.com/HugoRCD/shelve/issues){rel=""nofollow""} page. Provide as much detail as possible, including steps to reproduce the issue, screenshots, and any relevant error messages. ### Suggesting Features We welcome feature suggestions and ideas to improve Shelve. To suggest a new feature, please open an issue on our [GitHub Issues](https://github.com/HugoRCD/shelve/issues){rel=""nofollow""} page and provide a clear description of the feature, its benefits, and any potential use cases. ::tip{to="https://shelve.cloud/docs/contributing/dev-setup"} If you want to contribute to Shelve, but don't know how to run the project locally, check out the [Dev Setup](https://shelve.cloud/docs/contributing/dev-setup) guide. :: # Dev Setup #### Monorepo Structure Shelve uses a monorepo structure to manage multiple packages and applications. The main packages are: - `apps/shelve`: The main Shelve application. - `apps/lp`: The landing page for Shelve. - `apps/vault`: The vault application. - `apps/base`: This is the base layer that contains the shared configuration lp and shelve (components, plugins, etc). - `packages/cli`: The CLI connected to the Shelve application (push, pull, etc). - `packages/crypto`: The encryption and decryption package. - `packages/types`: The shared types inside the monorepo. - `packages/utils`: The shared utilities inside the monorepo. The monorepo is managed utilizing [Turborepo](https://turbo.build){rel=""nofollow""}. With Turborepo, it is possible to execute commands across all packages or target specific packages. Consequently, you will primarily operate from the root of the repository and execute commands such as `pnpm run dev:app` to initiate the Shelve application. #### Necessary Environment Variables - `DATABASE_URL` (PostgreSQL) - `NUXT_OAUTH_GITHUB_CLIENT_ID` (or google) - `NUXT_OAUTH_GITHUB_CLIENT_SECRET` (or google) - `NUXT_PRIVATE_ENCRYPTION_KEY` To set up the development environment for Shelve, follow these steps: ::steps ### Clone the Repository: ```sh [terminal] git clone https://github.com/HugoRCD/shelve.git && cd shelve ``` ### Install Dependencies: ```sh [terminal] pnpm install ``` ### Copy the Example Environment File: ```sh [terminal] cp apps/shelve/.env.example apps/shelve/.env ``` ### Update Environment Variables: Edit the `apps/shelve/.env` file and update the necessary environment variables. ### Start the Development Server: ```sh [terminal] pnpm run dev:app ``` :: # Introducing Shelve's Blog: Building in Public Welcome to Shelve's official blog! 👋 As Shelve continues to grow and evolve, I wanted to create a space where I could share more detailed insights about the project, its development, and the future I envision for it. ### Why Start a Blog? Building in public has always been part of Shelve's DNA. While Twitter is great for quick updates, some topics deserve a deeper dive. This blog will be the place for: - Technical deep-dives into Shelve's architecture - Behind-the-scenes of new features - Tutorials and best practices - Project updates and roadmap discussions ### What's Coming The next few months are exciting for Shelve. With the recent migration to Nuxt Hub, we're unlocking new possibilities that will transform how developers manage their environments. Expect articles about: - How Shelve leverages AI for smarter environment management - Deep dives into our new file upload system - Best practices for team environment sharing - And much more! ### Join the Journey Shelve started with a simple goal: make environment management less painful for developers. Today, it's growing into something much bigger, and I'm excited to share this journey with you. Whether you're a long-time user or just discovering Shelve, I hope this blog will give you valuable insights into modern development workflows and tools. Stay tuned for more articles, and feel free to reach out on [Twitter](https://twitter.com/HugoRCD__){rel=""nofollow""} with topics you'd like to see covered! Happy coding! ✨ # Announcing 2.0 ## Shelve 2.0: A Giant Leap Forward For a long time, Shelve has been a passion project, a side endeavor born from my own needs as a developer. But today, I'm incredibly proud to announce that Shelve is taking a monumental leap forward. **Shelve 2.0 is here, and it marks its transformation from a side project to a truly polished and feature-rich tool.** This isn't just an incremental update. I've poured my heart and soul into rebuilding Shelve from the ground up, focusing on: - **Professional Polish:** A brand new visual identity, logo, and dedicated [brand guidelines](https://shelve.cloud/brand). Shelve now looks and feels more professional than ever. - **Enhanced User Experience:** Every interaction has been refined for simplicity and elegance, making complex tasks feel intuitive. - **Powerful Core Features, Radically Simplified:** Shelve 2.0 is packed with features focused on environment management, now more accessible and user-friendly than ever. ## A New Era, A New Identity ✨ One of the most visible changes in Shelve 2.0 is the complete visual overhaul. I've invested heavily in crafting a brand identity that reflects the maturity and ambition of Shelve: - **Brand New Logo:** A modern, clean logo that embodies Shelve's core values of simplicity and efficiency. - **Dedicated Brand Guidelines:** Ensuring a consistent and professional experience across all platforms and touchpoints. - **Revamped Landing Page & Documentation:** A completely redesigned website built with Nuxt UI Pro, showcasing Shelve's polished new look and feel. This visual transformation isn't just cosmetic. It signals my commitment to building a tool that developers can rely on and be proud to use. ## Power & Simplicity, Reimagined 🚀 Shelve 2.0 is not just about looks. I've also massively enhanced the core functionality and developer experience around environment management: ### Say Goodbye to .env Files with `shelve run` One of the flagship features of v2.0 is the **`shelve run` command**. Imagine running any npm script (`dev`, `build`, `start`, etc.) directly from your terminal, **without needing a `.env` file**. Shelve 2.0 introduces runtime secrets injection, allowing you to access your environment variables securely stored in Shelve, directly within your scripts. This radically simplifies your workflow and eliminates a major source of configuration headaches. ### Seamlessly Push Variables to GitHub Secrets Taking integrations a step further, Shelve 2.0 now allows you to **push environment variables directly to your GitHub repositories as GitHub Secrets.** This is a game-changer for streamlining your CI/CD pipelines and managing configurations across your projects. Forget the cumbersome manual process of managing secrets directly in GitHub - Shelve makes it effortless. This integration is the first step towards deeper connections with other developer tools and platforms, expanding Shelve's reach beyond just environment variable management. ### Introducing Vault as a standalone project for secrets sharing Vault, previously a feature within the Shelve landing page, is now a **completely standalone application, accessible at [vault.shelve.cloud](https://vault.shelve.cloud){rel=""nofollow""}.** This self-hostable application allows anyone to securely share encrypted environment variables with others, independent of the main Shelve application. Vault provides a basic yet powerful solution for secure secret sharing, easily deployable and usable by anyone. ### Enhanced Core Features & Developer Experience Beyond these highlights, v2.0 is packed with improvements across the board: - **Drizzle ORM Migration:** I've migrated from Prisma to Drizzle ORM, enhancing performance and flexibility. - **Citty CLI Framework:** A switch to Citty provides a more robust and extensible CLI experience. - **GitHub Secrets Sync:** Effortlessly synchronize your environment variables with GitHub Secrets for enhanced security and streamlined workflows. - **Monorepo Workflow Improvements:** Enhanced support for monorepos, making Shelve even more versatile for complex projects. - **Real-time Visitor Stats:** A new module to track website visitors in real-time. - **Vault UI Revamp:** A more intuitive and user-friendly interface for managing your secrets. - **Global Stats Dashboard:** Gain insights into your Shelve usage with a new global statistics dashboard. - **Improved Integrations View:** A cleaner and more informative view for managing your integrations. - **And much more:** From view transitions to enhanced search and sorting, v2.0 is filled with refinements and new features designed to elevate your development experience. ## Under the Hood 🛠️ While the visual and functional improvements are front and center, Shelve 2.0 also includes significant under-the-hood refactoring and enhancements: - **Server Upgrade:** Major server-side improvements for performance and stability. - **Codebase Refactoring:** Extensive codebase cleanup and refactoring for maintainability and scalability. - **Documentation Overhaul:** Comprehensive documentation updates and improvements, now integrated directly into the landing page for easier access. - **Testing & Bug Fixes:** Numerous bug fixes and stability improvements to ensure a rock-solid experience. ## Try Shelve 2.0 Today! 🚀 Shelve 2.0 is a massive step forward, and I'm incredibly excited for you to experience it. Whether you're a solo developer or part of a team, Shelve 2.0 is designed to streamline your environment management and empower you to build amazing things. - **Visit the Website:** [shelve.cloud](https://shelve.cloud) - **Explore the Documentation:** [shelve.cloud/docs/getting-started](https://shelve.cloud/docs/getting-started) - **Contribute on GitHub:** [github.com/HugoRCD/shelve](https://github.com/HugoRCD/shelve){rel=""nofollow""} 💚 Hugo # Crafting Clarity: Introducing the New Shelve Landing Page ## A New Foundation for Shelve Today, I'm excited to introduce a significant step forward for Shelve: a completely redesigned landing page experience at [shelve.cloud](https://shelve.cloud){rel=""nofollow""}. This project was driven by the desire to create an interface that truly reflects the core principles guiding Shelve itself – **simplicity, security, elegance, and a deep focus on Developer Experience (DX).** ## Aligning Perception with Ambition As Shelve continues to evolve, aiming to be a foundational tool in the developer workflow, it felt essential that its primary touchpoint – the landing page – accurately reflected this ambition and the level of quality I strive for in the product. The goals for this redesign were clear: - **Establish Clarity & Coherence:** Immediately convey Shelve's value in making secrets management effortless and secure. - **Build Trust:** Present a polished, professional identity that inspires confidence. - **Ensure Coherence:** Align the messaging, visuals, and overall feel with the product's philosophy. - **Elevate the Experience:** Apply meticulous attention to detail in design and interaction, making the page itself a pleasure to use. The result is a landing page and documentation section that feels more focused, refined, and fundamentally *Shelve*. It provides a clear, coherent foundation that accurately represents where the project is today and sets the stage for its future. ## The Craft: Elevating the Experience Achieving the level of polish and specific design vision I aimed for with this new landing page required not just meticulous planning but also leveraging tools that could accelerate the process without compromising quality or flexibility. While Shelve has always utilized the Nuxt ecosystem, this redesign was an opportunity to lean heavily into the capabilities offered by **Nuxt UI** and especially **Nuxt UI Pro**. The challenge often lies in balancing speed with originality. Building every intricate detail, managing responsiveness across devices, ensuring accessibility, and handling theming for light/dark modes from scratch is incredibly time-consuming. Component libraries like Nuxt UI Pro offer a significant advantage by providing **well-architected, production-ready building blocks** for many common (and complex) UI patterns. This instantly addresses a large portion of the foundational work. However, the true value unlocked for this project wasn't just using pre-built components, but the **depth of customization** they allow. The goal was never to adopt a generic template, but to craft a unique visual identity for Shelve. Nuxt UI Pro facilitates this by enabling extensive tailoring – modifying styles, overriding structures, and integrating custom interactions – all while retaining the benefits of the underlying framework (like type-safety and developer experience). This meant I could **focus creative energy on the unique aspects** – the specific animations using Motion One, the precise layout adjustments, the custom visual effects – rather than rebuilding standard interface elements. It significantly shortens the path to a sophisticated, bespoke result that truly feels aligned with the brand. This entire process underscores a philosophy: powerful tools should enable creativity and efficiency, not restrict them. In this spirit, **the complete codebase for the landing page and documentation is open-source**, available in the main [Shelve repository](https://github.com/HugoRCD/shelve){rel=""nofollow""}. It serves as a tangible example of how these libraries can be pushed and customized to build premium, distinct web experiences. Feel free to explore how components are adapted and complex layouts are achieved. It’s often in the finer points and layers beneath the surface where the most interesting details reside, rewarding careful exploration. [H1dd3n\_\_P4yl04d!]{.text-default}