{"id":13467915,"url":"https://github.com/supabase/vault","last_synced_at":"2025-04-13T08:41:17.860Z","repository":{"id":65020965,"uuid":"524821089","full_name":"supabase/vault","owner":"supabase","description":"Extension for storing encrypted secrets in the Vault","archived":false,"fork":false,"pushed_at":"2025-01-22T10:41:11.000Z","size":59,"stargazers_count":193,"open_issues_count":18,"forks_count":10,"subscribers_count":21,"default_branch":"main","last_synced_at":"2025-04-11T21:21:34.770Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Nix","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/supabase.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["supabase"],"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"custom":null}},"created_at":"2022-08-15T01:28:19.000Z","updated_at":"2025-04-04T02:25:34.000Z","dependencies_parsed_at":"2024-11-06T14:30:12.702Z","dependency_job_id":null,"html_url":"https://github.com/supabase/vault","commit_stats":{"total_commits":29,"total_committers":6,"mean_commits":4.833333333333333,"dds":0.5862068965517242,"last_synced_commit":"212474a76c740139991fdcb6363f4e1c07e1da60"},"previous_names":[],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/supabase%2Fvault","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/supabase%2Fvault/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/supabase%2Fvault/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/supabase%2Fvault/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/supabase","download_url":"https://codeload.github.com/supabase/vault/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248686546,"owners_count":21145511,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T15:01:02.498Z","updated_at":"2025-04-13T08:41:17.835Z","avatar_url":"https://github.com/supabase.png","language":"Nix","funding_links":["https://github.com/sponsors/supabase"],"categories":["Nix"],"sub_categories":[],"readme":"# Introduction to the Vault (Beta)\n\nMany applications have sensitive data that must have additional\nstorage protection relative to other data.  For example, your\napplication may access external services with an \"API Key\".  This key\nis issued to you by that specific external service provider, and you\nmust keep it safe from being stolen or leaked.  If someone got their\nhands on your payment processor key, for example, they may be able to\nuse it to send money or digital assets out of your account to someone\nelse.  Wherever this key is stored, it would make sense to store it in\nan encrypted form.\n\nSupabase provides a table called `vault.secrets` that can be used to\nstore sensitive information like API keys.  These secrets will be\nstored in an encrypted format on disk and in any database dumps.  This\nis often called [Encryption At\nRest](https://en.wikipedia.org/wiki/Data_at_rest).  Decrypting this\ntable is done through a special database view called\n`vault.decrypted_secrets` that uses an encryption key that is itself\nnot avaiable to SQL, but can be referred to by ID.  Supabase manages\nthese internal keys for you, so you can't leak them out of the\ndatabase, you can only refer to them by their ids.\n\n## Installation\n\nThe Vault extension is enabled by default. If you install the Vault \nyourself locally, from SQL you can do:\n\n```\nCREATE EXTENSION supabase_vault CASCADE;\n```\n\n## Using the Vault\n\nUsing the vault is as simple as `INSERT`ing data into the\n`vault.secret` table.\n\n```\npostgres=\u003e INSERT INTO vault.secrets (secret) VALUES ('s3kre3t_k3y') RETURNING *;\n-[ RECORD 1 ]-------------------------------------------------------------\nid          | d91596b8-1047-446c-b9c0-66d98af6d001\nname        | \ndescription | \nsecret      | S02eXS9BBY+kE3r621IS8beAytEEtj+dDHjs9/0AoMy7HTbog+ylxcS22A==\nkey_id      | 7f5ad44b-6bd5-4c99-9f68-4b6c7486f927\nnonce       | \\x3aa2e92f9808e496aa4163a59304b895\ncreated_at  | 2022-12-14 02:29:21.3625+00\nupdated_at  | 2022-12-14 02:29:21.3625+00\n```\n\nThere is also a handy function for creating secrets called\n`vault.create_secret()`:\n\n```\npostgres=\u003e select vault.create_secret('another_s3kre3t');\n-[ RECORD 1 ]-+-------------------------------------\ncreate_secret | c9b00867-ca8b-44fc-a81d-d20b8169be17\n\n```\n\nThe function returns the UUID of the new secret.\n\n## Name and Description\n\nSecrets can also have an optional _unique_ name, or an optional\ndescription.  These are also arguments to `vault.create_secret()`:\n\n```\npostgres=\u003e select vault.create_secret('another_s3kre3t', 'unique_name', 'This is the description');\n-[ RECORD 1 ]-+-------------------------------------\ncreate_secret | 7095d222-efe5-4cd5-b5c6-5755b451e223\n\npostgres=\u003e select * from vault.secrets where id = '7095d222-efe5-4cd5-b5c6-5755b451e223';\n-[ RECORD 1 ]-----------------------------------------------------------------\nid          | 7095d222-efe5-4cd5-b5c6-5755b451e223\nname        | unique_name\ndescription | This is the description\nsecret      | 3mMeOcoG84a5F2uOfy2ugWYDp9sdxvCTmi6kTeT97bvA8rCEsG5DWWZtTU8VVeE=\nkey_id      | c62da7a0-b85d-471d-8ea7-52aae21d7354\nnonce       | \\x9f2d60954ba5eb566445736e0760b0e3\ncreated_at  | 2022-12-14 02:34:23.85159+00\nupdated_at  | 2022-12-14 02:34:23.85159+00\n```\n\n## Querying Data from the Vault\n\nIf you look in the `vault.secrets` table, you will see that your data\nis stored encrypted. To decrypt the data, there is an automatically\ncreated view `vault.decrypted_secrets`.  This view will decrypt secret\ndata on the fly:\n\n```\npostgres=\u003e select * from vault.decrypted_secrets order by created_at desc limit 3;\n-[ RECORD 1 ]----+-----------------------------------------------------------------\nid               | 7095d222-efe5-4cd5-b5c6-5755b451e223\nname             | unique_name\ndescription      | This is the description\nsecret           | 3mMeOcoG84a5F2uOfy2ugWYDp9sdxvCTmi6kTeT97bvA8rCEsG5DWWZtTU8VVeE=\ndecrypted_secret | another_s3kre3t\nkey_id           | c62da7a0-b85d-471d-8ea7-52aae21d7354\nnonce            | \\x9f2d60954ba5eb566445736e0760b0e3\ncreated_at       | 2022-12-14 02:34:23.85159+00\nupdated_at       | 2022-12-14 02:34:23.85159+00\n-[ RECORD 2 ]----+-----------------------------------------------------------------\nid               | c9b00867-ca8b-44fc-a81d-d20b8169be17\nname             | \ndescription      | \nsecret           | a1CE4vXwQ53+N9bllJj1D7fasm59ykohjb7K90PPsRFUd9IbBdxIGZNoSQLIXl4=\ndecrypted_secret | another_s3kre3t\nkey_id           | 8c72b05e-b931-4372-abf9-a09cfad18489\nnonce            | \\x1d3b2761548c4efb2d29ca11d44aa22f\ncreated_at       | 2022-12-14 02:32:50.58921+00\nupdated_at       | 2022-12-14 02:32:50.58921+00\n-[ RECORD 3 ]----+-----------------------------------------------------------------\nid               | d91596b8-1047-446c-b9c0-66d98af6d001\nname             | \ndescription      | \nsecret           | S02eXS9BBY+kE3r621IS8beAytEEtj+dDHjs9/0AoMy7HTbog+ylxcS22A==\ndecrypted_secret | s3kre3t_k3y\nkey_id           | 7f5ad44b-6bd5-4c99-9f68-4b6c7486f927\nnonce            | \\x3aa2e92f9808e496aa4163a59304b895\ncreated_at       | 2022-12-14 02:29:21.3625+00\nupdated_at       | 2022-12-14 02:29:21.3625+00\n```\n\nNotice how this view has a `decrypted_secret` column that contains the\ndecrypted secrets.  Views are not stored on disk, they are only run at\nquery time, so the secret remains encrypted on disk, and in any backup\ndumps or replication streams.\n\nYou should ensure that you protect access to this view with the\nappropriate SQL privilege settings at all times, as anyone that has\naccess to the view has access to decrypted secrets.\n\n## Updating Secrets\n\nA secret can be updated with the `vault.update_secret()` function,\nthis function makes updating secrets easy, just provide the secret\nUUID as the first argument, and then an updated secret, updated\noptional unique name, or updated description:\n\n```\npostgres=\u003e select vault.update_secret('7095d222-efe5-4cd5-b5c6-5755b451e223', 'n3w_upd@ted_s3kret', \n    'updated_unique_name', 'This is the updated description');\n-[ RECORD 1 ]-+-\nupdate_secret | \n\npostgres=\u003e select * from vault.decrypted_secrets where id = '7095d222-efe5-4cd5-b5c6-5755b451e223';\n-[ RECORD 1 ]----+---------------------------------------------------------------------\nid               | 7095d222-efe5-4cd5-b5c6-5755b451e223\nname             | updated_unique_name\ndescription      | This is the updated description\nsecret           | lhb3HBFxF+qJzp/HHCwhjl4QFb5dYDsIQEm35DaZQOovdkgp2iy6UMufTKJGH4ThMrU=\ndecrypted_secret | n3w_upd@ted_s3kret\nkey_id           | c62da7a0-b85d-471d-8ea7-52aae21d7354\nnonce            | \\x9f2d60954ba5eb566445736e0760b0e3\ncreated_at       | 2022-12-14 02:34:23.85159+00\nupdated_at       | 2022-12-14 02:51:13.938396+00\n```\n\n## Internal Details\n\nTo encrypt data, you need a *key id*.  You can use the default key id\ncreated automatically for every project, or create your own key ids\nUsing the `pgsodium.create_key()` function.  Key ids are used to\ninternally derive the encryption key used to encrypt secrets in the\nvault.  Vault users typically do not have access to the key itself,\nonly the key id.\n\nBoth `vault.create_secret()` and `vault.update_secret()` take an\noptional fourth `new_key_id` argument.  This argument can be used to\nstore a different key id for the secret instead of the default value.\n\n```\npostgres=\u003e select vault.create_secret('another_s3kre3t_key', 'another_unique_name', \n   'This is another description', (pgsodium.create_key()).id);\n-[ RECORD 1 ]-+-------------------------------------\ncreate_secret | cec9e005-a44d-4b19-86e1-febf3cd40619\n```\n\nWhich roles should have access to the `vault.secrets` table should be\ncarefully considered.  There are two ways to grant access, the first\nis that the `postgres` user can explicitly grant access to the vault\ntable itself.\n\n## Turning off Statement Logging\n\nWhen you insert secrets into the vault table with an INSERT statement,\nthose statements get logged by default into the Supabase logs.  Since\nthis would mean your secrets are stored unencrypted in the logs, you\nshould turn off statement logging while using the Vault.\n\nWhile turning off statement logging does hinder you if you're used to\nlooking at the logs to debug your application, it provides a much\nhigher level of security by ensuring that your data does not leak out\nof the database and into the logs.  This is especially critical with\nencrypted column data, because the statement logs will contain the\n*unencrypted* secrets.  If you *must* store that data encrypted, then\nyou *must* turn off statement logging.\n\n```\nALTER SYSTEM SET statement_log = 'none';\n```\n\nAnd then restart your project from the dashboard to enable that\nchange.\n\nIn the future we are researching various ways to refine the way\nstatement logging interacts with sensitive columns.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsupabase%2Fvault","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsupabase%2Fvault","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsupabase%2Fvault/lists"}