> ## Documentation Index
> Fetch the complete documentation index at: https://docs.basaltic.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Reading and writing the value

> Why the value has its own endpoint, and how versions accumulate behind it.

## The value is a separate endpoint

Describing a secret and reading it are different operations with different
permissions.

<Columns cols={2}>
  <Card title="GET /v1/secrets/{id}" icon="file-text">
    Name, description, tags, current version number, which KMS key it is bound
    to, deletion state. **Never the value.** Guarded by
    `secrets:DescribeSecret`.
  </Card>

  <Card title="GET /v1/secrets/{id}/value" icon="eye">
    The decrypted plaintext, base64-encoded. Guarded by the separate,
    narrower `secrets:GetSecretValue`.
  </Card>
</Columns>

This split is the point of the service. A deployment tool, a dashboard or an
inventory job can be given `secrets:DescribeSecret` across everything and still
be unable to read a single value.

<Note>
  **Every plaintext read is recorded, successful or not.** A denied read, a
  probe for an id the caller does not own, and a decrypt that failed are all
  written to the audit trail with the error code that stopped them — that set
  is exactly what a review is looking for, and it would be invisible if only
  successes were logged.
</Note>

Add `?version=N` to read a specific version instead of the current one.

<Note>
  The console's **Reveal** button, on each row of a secret's **Versions** tab,
  calls this endpoint. It is the same operation with the same permission and
  the same audit record — opening a secret in a browser is not a quieter way to
  read it.
</Note>

## Versions

<Tabs>
  <Tab title="Console">
    Open the secret from **Secrets** and choose **Put New Value**. The **Put
    new value** dialog takes the **New value**; **Store new version** writes
    it and it becomes current.

    Every version stays listed on the **Versions** tab, with the newest marked
    **Current**.
  </Tab>

  <Tab title="API">
    ```bash theme={null}
    POST /v1/secrets/{secret_id}/value
    { "value": "bmV3LXNlY3JldC12YWx1ZQ==" }
    ```
  </Tab>

  <Tab title="CLI">
    ```bash theme={null}
    basaltic secrets set-value <secret-id> --value "$(base64 -w0 < new-value.txt)"
    ```

    Reading the current value back is `basaltic secrets get-value <secret-id>`,
    with `--version` for an older one.
  </Tab>

  <Tab title="Go">
    ```go theme={null}
    v, err := secrets.New(cfg).PutSecretValue(ctx, secretID, &secrets.PutSecretValueRequest{
        Value: []byte("new-secret-value"),
    })
    ```

    `v.Version` is the number just allocated, which is what to record if you
    need to roll back to the one before it.
  </Tab>
</Tabs>

Each call allocates the next version number and makes it current; the previous
version stops being current but stays readable by explicit `?version=`. That is
what makes rotation safe — you publish the new value, let consumers pick it up,
and still have the old one to fall back to.

`GET /v1/secrets/{secret_id}/versions` lists version metadata, highest first.
Each entry carries `created_by`, the CRN of the principal that wrote it
(`crn:iam:::user/<id>` or `crn:iam:::service-account/<id>`), so you can see who
rotated what.

<Note>
  Version metadata never includes ciphertext. `GET .../value` is the only route
  that produces plaintext, and it is the only one that needs
  `secrets:GetSecretValue`.
</Note>
