Getting started
- Open Vault in the Console and make a vault per app or environment, such as
app-production - Add a secret or create a key
- Under Access, give a role to the service account your service uses
- In your code, get a token and call the API as shown below
Service account roles
| Role | Allows |
|---|---|
| Read secrets | Reading secret values |
| Read and write secrets | Reading values and adding versions |
| Encrypt and sign with keys | Encrypt, decrypt, sign and verify |
| Use and rotate keys | Using keys and making new versions |
| Read secrets and use keys | Both of those |
| Read and write secrets, use and rotate keys | Everything |
Without a role a service account cannot use a vault, even in the same Project. For people in the Project: Viewers and Operators see names and activity, Editors can see and change values, and Owners and Admins can delete.
A secret as a Container or Function variable
Set a variable to vault://app-production/db-password, or click Use a Vault value on the variables page. Your code reads the variable as usual, with no change. The value is opened when the service starts; the Console and revisions keep only this address.
First give the service's service account a role that reads secrets. After adding a version, restart the service to get the new value. To pin a version, add its number, as in vault://app-production/db-password/2. If the value cannot be read at start, say because access was removed, the service does not start and says why.
Reading a secret
# Inside a Container or Function: a token from the machine it runs on
TOKEN=$(curl -s -H "Metadata: true" \
"http://169.254.169.254/identity/oauth2/token" | jq -r .access_token)
curl -H "Authorization: Bearer $TOKEN" \
https://api.thailandhosting.com/v1/vaults/app-production/secrets/db-password
# {"name":"db-password","version":2,"value":"...","expiresAt":null}With no version you get the newest one that is enabled and has not expired. Ask for one with ?version=2. Keep the value in your program's memory for a while instead of calling each time.
Encrypting with a key
curl -X POST -H "Authorization: Bearer $TOKEN" \
-d "{\"plaintext\":\"$(printf 'hello' | base64)\"}" \
https://api.thailandhosting.com/v1/vaults/app-production/keys/customer-data/encrypt
# {"ciphertext":"thv1:2:...","version":2}
curl -X POST -H "Authorization: Bearer $TOKEN" \
-d '{"ciphertext":"thv1:2:..."}' \
https://api.thailandhosting.com/v1/vaults/app-production/keys/customer-data/decrypt
# {"plaintext":"aGVsbG8="}Send data as base64, up to 64 KB at a time. The result names its key version, so it still decrypts after a rotation. If you send aad when encrypting, send the same value when decrypting.
For large or numerous items, ask /data-key for a data key, encrypt in your program with its plaintext, and store its ciphertext beside the data. To read, send that ciphertext to decrypt.
Key types
| Type | Used for |
|---|---|
| AES-256-GCM | Encrypt / decrypt |
| RSA 2048, RSA 4096 | One of: encrypt / decrypt, or sign / verify |
| ECDSA P-256, P-384 | Sign / verify |
SDK
import { defaultCredential } from '@thailandhosting/identity';
const vault = defaultCredential().vault('app-production');
const secret = await vault.getSecret('db-password');
const ciphertext = await vault.encrypt('customer-data', 'hello');
const plaintext = await vault.decrypt('customer-data', ciphertext);from thailandhosting_identity import default_credential
vault = default_credential().vault("app-production")
secret = vault.get_secret("db-password")
ciphertext = vault.encrypt("customer-data", b"hello")
plaintext = vault.decrypt("customer-data", ciphertext)cred, err := identity.Default()
vault := cred.Vault("app-production")
secret, err := vault.GetSecret(ctx, "db-password")
ciphertext, err := vault.Encrypt(ctx, "customer-data", []byte("hello"), nil)
plaintext, err := vault.Decrypt(ctx, "customer-data", ciphertext, nil)th CLI
th vault create app-production
th vault secret set app-production db-password # reads the value from standard input
th vault secret get app-production db-password
th vault key create app-production customer-data --type aes256 --rotation-days 90
th vault key encrypt app-production customer-data 'hello'th vault acts as whoever ran th auth login. API keys cannot call it, so automation and CI use a service account. Purging is only in the Console.
Pricing
A vault itself is free. You pay for the secrets and keys you keep (all versions included) and for each time a program reads a secret or uses a key. See Pricing for rates.
Free: reading a value as a variable when a service starts, the Console and the th CLI, listing, adding versions and rotating keys, calls that fail, and deleted items. If a Project has unpaid usage, programs cannot call its vaults until it is topped up.
Deleting and recovering
Deleted items stop working at once and can be recovered within the period set on the vault (7-90 days). Then they are purged. Once a key is purged, anything encrypted with it can never be opened again.
Turn on purge protection so nobody can purge before the period ends, not even the Project owner. It cannot be turned off.
Activity
The Activity tab shows who did what, when and from which IP, including refused calls. A program's uses are grouped into one line per hour per caller, and kept for 1 year.
If something goes wrong
I get 403 forbidden
The service account has no role that allows this call. The Activity tab shows which name was refused. Give it a role under Access.
Are keys kept in an HSM?
No. Keys are encrypted in software and stored in the system's database. There is no HSM option yet.