[{"data":1,"prerenderedAt":460},["ShallowReactive",2],{"navigation":3,"docus_navigation_docs":147,"-docs-core-features-encryption":183,"-docs-core-features-encryption-surround":455},[4,130],{"title":5,"path":6,"stem":7,"children":8,"page":55},"Docs","\u002Fdocs","docs",[9,21,56,95,105,119],{"title":10,"path":11,"stem":12,"children":13,"icon":20},"Getting Started","\u002Fdocs\u002Fgetting-started","docs\u002F1.getting-started\u002F1.index",[14,16],{"title":15,"path":11,"stem":12},"Introduction",{"title":17,"path":18,"stem":19},"Quickstart","\u002Fdocs\u002Fgetting-started\u002Fquickstart","docs\u002F1.getting-started\u002F2.quickstart","i-lucide-rocket",{"title":22,"icon":23,"path":24,"stem":25,"children":26,"page":55},"Core Features","i-lucide-tag","\u002Fdocs\u002Fcore-features","docs\u002F2.core-features",[27,31,35,39,43,47,51],{"title":28,"path":29,"stem":30},"Audit Logs","\u002Fdocs\u002Fcore-features\u002Faudit-logs","docs\u002F2.core-features\u002Faudit-logs",{"title":32,"path":33,"stem":34},"Encryption","\u002Fdocs\u002Fcore-features\u002Fencryption","docs\u002F2.core-features\u002Fencryption",{"title":36,"path":37,"stem":38},"Environments","\u002Fdocs\u002Fcore-features\u002Fenvironments","docs\u002F2.core-features\u002Fenvironments",{"title":40,"path":41,"stem":42},"Projects","\u002Fdocs\u002Fcore-features\u002Fprojects","docs\u002F2.core-features\u002Fprojects",{"title":44,"path":45,"stem":46},"Teams","\u002Fdocs\u002Fcore-features\u002Fteams","docs\u002F2.core-features\u002Fteams",{"title":48,"path":49,"stem":50},"API Tokens","\u002Fdocs\u002Fcore-features\u002Ftokens","docs\u002F2.core-features\u002Ftokens",{"title":52,"path":53,"stem":54},"Variables","\u002Fdocs\u002Fcore-features\u002Fvariables","docs\u002F2.core-features\u002Fvariables",false,{"title":57,"icon":58,"path":59,"stem":60,"children":61},"CLI","i-lucide-terminal","\u002Fdocs\u002Fcli","docs\u002F3.cli\u002F2.index",[62,63,67,71,75,79,83,87,91],{"title":15,"path":59,"stem":60},{"title":64,"path":65,"stem":66},"Init","\u002Fdocs\u002Fcli\u002Finit","docs\u002F3.cli\u002F1.init",{"title":68,"path":69,"stem":70},"Run","\u002Fdocs\u002Fcli\u002Frun","docs\u002F3.cli\u002F3.run",{"title":72,"path":73,"stem":74},"Login and Logout","\u002Fdocs\u002Fcli\u002Flogin-logout","docs\u002F3.cli\u002F4.login-logout",{"title":76,"path":77,"stem":78},"Push and Pull","\u002Fdocs\u002Fcli\u002Fpush-pull","docs\u002F3.cli\u002F5.push-pull",{"title":80,"path":81,"stem":82},"Create","\u002Fdocs\u002Fcli\u002Fcreate","docs\u002F3.cli\u002F6.create",{"title":84,"path":85,"stem":86},"Config","\u002Fdocs\u002Fcli\u002Fconfig","docs\u002F3.cli\u002F7.config",{"title":88,"path":89,"stem":90},"Generate","\u002Fdocs\u002Fcli\u002Fgenerate","docs\u002F3.cli\u002F8.generate",{"title":92,"path":93,"stem":94},"Upgrade","\u002Fdocs\u002Fcli\u002Fupgrade","docs\u002F3.cli\u002F9.upgrade",{"title":96,"icon":97,"path":98,"stem":99,"children":100,"page":55},"Integrations","i-lucide-blocks","\u002Fdocs\u002Fintegrations","docs\u002F4.integrations",[101],{"title":102,"path":103,"stem":104},"Github","\u002Fdocs\u002Fintegrations\u002Fgithub","docs\u002F4.integrations\u002F1.github",{"title":106,"icon":107,"path":108,"stem":109,"children":110,"page":55},"Self-hosting","i-lucide-cloud","\u002Fdocs\u002Fself-hosting","docs\u002F5.self-hosting",[111,115],{"title":112,"path":113,"stem":114},"Deploy on Vercel","\u002Fdocs\u002Fself-hosting\u002Fvercel","docs\u002F5.self-hosting\u002F1.vercel",{"title":116,"path":117,"stem":118},"Environment variables","\u002Fdocs\u002Fself-hosting\u002Fenvironment-variables","docs\u002F5.self-hosting\u002F2.environment-variables",{"title":120,"path":121,"stem":122,"children":123,"icon":129},"Contributing","\u002Fdocs\u002Fcontributing","docs\u002F6.contributing\u002F1.index",[124,125],{"title":15,"path":121,"stem":122},{"title":126,"path":127,"stem":128},"Dev Setup","\u002Fdocs\u002Fcontributing\u002Fdev-setup","docs\u002F6.contributing\u002F2.dev-setup","i-lucide-heart-handshake",{"title":131,"path":132,"stem":133,"children":134,"page":55},"Blog","\u002Fblog","blog",[135,139,143],{"title":136,"path":137,"stem":138},"Introducing Shelve's Blog: Building in Public","\u002Fblog\u002Fintroducing-shelve-blog","blog\u002F01.introducing-shelve-blog",{"title":140,"path":141,"stem":142},"Announcing 2.0","\u002Fblog\u002Fannouncing-2.0","blog\u002F02.announcing-2.0",{"title":144,"path":145,"stem":146},"Crafting Clarity: Introducing the New Shelve Landing Page","\u002Fblog\u002Fnew-landing-page","blog\u002F03.new-landing-page",[148,152,161,172,175,179],{"title":10,"path":11,"stem":12,"children":149,"icon":20},[150,151],{"title":15,"path":11,"stem":12},{"title":17,"path":18,"stem":19},{"title":22,"icon":23,"path":24,"stem":25,"children":153,"page":55},[154,155,156,157,158,159,160],{"title":28,"path":29,"stem":30},{"title":32,"path":33,"stem":34},{"title":36,"path":37,"stem":38},{"title":40,"path":41,"stem":42},{"title":44,"path":45,"stem":46},{"title":48,"path":49,"stem":50},{"title":52,"path":53,"stem":54},{"title":57,"icon":58,"path":59,"stem":60,"children":162,"page":-1},[163,164,165,166,167,168,169,170,171],{"title":15,"path":59,"stem":60},{"title":64,"path":65,"stem":66},{"title":68,"path":69,"stem":70},{"title":72,"path":73,"stem":74},{"title":76,"path":77,"stem":78},{"title":80,"path":81,"stem":82},{"title":84,"path":85,"stem":86},{"title":88,"path":89,"stem":90},{"title":92,"path":93,"stem":94},{"title":96,"icon":97,"path":98,"stem":99,"children":173,"page":55},[174],{"title":102,"path":103,"stem":104},{"title":106,"icon":107,"path":108,"stem":109,"children":176,"page":55},[177,178],{"title":112,"path":113,"stem":114},{"title":116,"path":117,"stem":118},{"title":120,"path":121,"stem":122,"children":180,"icon":129},[181,182],{"title":15,"path":121,"stem":122},{"title":126,"path":127,"stem":128},{"id":184,"title":32,"body":185,"description":449,"extension":450,"meta":451,"navigation":452,"path":33,"seo":453,"stem":34,"__hash__":454},"docs\u002Fdocs\u002F2.core-features\u002Fencryption.md",{"type":186,"value":187,"toc":439},"minimark",[188,192,197,200,211,244,261,265,297,301,308,312,378,382,388,392,399,403],[189,190,191],"p",{},"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.",[193,194,196],"h2",{"id":195},"two-tier-envelope-encryption","Two-tier envelope encryption",[189,198,199],{},"Secret values are never encrypted directly with the global server key. Instead Shelve uses an envelope scheme:",[201,202,207],"pre",{"className":203,"code":205,"language":206},[204],"language-text","value ──seal──▶ ciphertext     (key = DEK, per-project)\nDEK   ──seal──▶ encryptedDek   (key = KEK, platform-wide)\n","text",[208,209,205],"code",{"__ignoreMap":210},"",[212,213,214,231],"ul",{},[215,216,217,221,222,226,227,230],"li",{},[218,219,220],"strong",{},"KEK"," — ",[223,224,225],"em",{},"Key Encryption Key",". A single secret sourced from ",[208,228,229],{},"NUXT_PRIVATE_ENCRYPTION_KEY"," at boot. It never touches stored data directly; it is only used to seal and unseal DEKs.",[215,232,233,221,236,239,240,243],{},[218,234,235],{},"DEK",[223,237,238],{},"Data Encryption Key",". Generated server-side on a project's first write (32 random bytes, base64-encoded), sealed with the KEK, and persisted in ",[208,241,242],{},"projects.encryptedDek",". From then on every variable on that project is encrypted with that DEK.",[189,245,246,247,256,257,260],{},"The sealing primitive underneath is ",[248,249,253],"a",{"href":250,"rel":251},"https:\u002F\u002Fgithub.com\u002Fbrc-dd\u002Firon-webcrypto",[252],"nofollow",[208,254,255],{},"iron-webcrypto",", configured with authenticated encryption (",[208,258,259],{},"aes-256-gcm","). A tampered ciphertext fails to decrypt — there is no silent downgrade.",[193,262,264],{"id":263},"why-envelope-encryption","Why envelope encryption?",[266,267,268,279,288],"card-group",{},[269,270,272,276],"card",{"icon":271},"i-lucide-zap",[273,274,275],"template",{"v-slot:title":210},"Fast key rotation",[273,277,278],{"v-slot:description":210},"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.",[269,280,282,285],{"icon":281},"i-lucide-shield",[273,283,284],{"v-slot:title":210},"Scoped blast radius",[273,286,287],{"v-slot:description":210},"A leaked DEK compromises one project, not every secret on the instance. The KEK never leaves the server.",[269,289,291,294],{"icon":290},"i-lucide-layers",[273,292,293],{"v-slot:title":210},"BYOK-ready",[273,295,296],{"v-slot:description":210},"The DEK layer is the seam where hardware-backed key storage (KMS, HSM, passkeys) will plug in without touching application code.",[193,298,300],{"id":299},"backward-compatibility","Backward compatibility",[189,302,303,304,307],{},"Projects created before the envelope upgrade have no ",[208,305,306],{},"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.",[193,309,311],{"id":310},"what-is-stored-in-the-database","What is stored in the database",[313,314,315,328],"table",{},[316,317,318],"thead",{},[319,320,321,325],"tr",{},[322,323,324],"th",{},"Column",[322,326,327],{},"What it holds",[329,330,331,342,355,368],"tbody",{},[319,332,333,339],{},[334,335,336],"td",{},[208,337,338],{},"variables.encryptedValue",[334,340,341],{},"Sealed ciphertext of the secret value. Never plaintext.",[319,343,344,348],{},[334,345,346],{},[208,347,242],{},[334,349,350,351,354],{},"Project DEK, sealed by the KEK. ",[208,352,353],{},"null"," for pre-envelope projects.",[319,356,357,362],{},[334,358,359],{},[208,360,361],{},"tokens.hash",[334,363,364,367],{},[208,365,366],{},"sha256(token)"," as hex. The plaintext token is never stored.",[319,369,370,375],{},[334,371,372],{},[208,373,374],{},"tokens.prefix",[334,376,377],{},"Non-secret 12-char prefix used for display and audit logs.",[193,379,381],{"id":380},"api-tokens","API tokens",[189,383,384,385,387],{},"API tokens follow a different (but compatible) model: they are hashed, not encrypted. See ",[248,386,48],{"href":49}," for the full story.",[193,389,391],{"id":390},"in-transit","In transit",[189,393,394,395,398],{},"Traffic to ",[208,396,397],{},"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.",[193,400,402],{"id":401},"self-host-checklist","Self-host checklist",[404,405,406,413,419,430],"ol",{},[215,407,408,409,412],{},"Generate a strong KEK: ",[208,410,411],{},"openssl rand -base64 48",".",[215,414,415,416,418],{},"Set it as ",[208,417,229],{}," on your platform.",[215,420,421,424,425,412],{},[218,422,423],{},"Never"," rotate the KEK in-place without re-sealing existing DEKs — existing data becomes unreadable. A safe rotation procedure is on the ",[248,426,429],{"href":427,"rel":428},"https:\u002F\u002Fgithub.com\u002FHugoRCD\u002Fshelve\u002Fissues",[252],"roadmap",[215,431,432,433,435,436,438],{},"Back up ",[208,434,242],{}," alongside ",[208,437,338],{},"; losing either makes the corresponding data unrecoverable.",{"title":210,"searchDepth":440,"depth":440,"links":441},2,[442,443,444,445,446,447,448],{"id":195,"depth":440,"text":196},{"id":263,"depth":440,"text":264},{"id":299,"depth":440,"text":300},{"id":310,"depth":440,"text":311},{"id":380,"depth":440,"text":381},{"id":390,"depth":440,"text":391},{"id":401,"depth":440,"text":402},"How Shelve protects secrets at rest and in transit.","md",{},true,{"title":32,"description":449},"TxTey8k-c2YsrVp3XM5-mHQV8YMOD3AJwlRynX65Nao",[456,458],{"title":28,"path":29,"stem":30,"description":457,"children":-1},"Review every privileged action performed on your teams, projects, and secrets.",{"title":36,"path":37,"stem":38,"description":459,"children":-1},"Learn more about environments in Shelve",1776787533560]