# mountOS Skill: Create and use volumes

> Point a storage at an object bucket, create a volume, mint an S3-style access key, mount it, then use forks, time travel, versioning, and recently-deleted.

This skill assumes the HUB is up and the target region has a ready cluster. If you
still need to stand those up, do that first (see Read next), then come back here.
Everything below runs against the Admin API at `<HUB>/api/v1/*`, signed with the
admin Ed25519 key.

```ts
import { createServerClient } from '@mountos-io/admin-sdk'

const client = createServerClient({
  baseUrl: 'https://hub.example.com',          // the HUB domain
  privateKey: process.env.MOUNTOS_PRIVATE_KEY, // admin Ed25519 seed (base64)
})
```

## 1. Create a storage (point at the object bucket)

A storage is a region-scoped pointer at one object bucket. The volume's file
content lands there; metadata stays in the region database. `storageType:'object'`
and `providerType:'s3'` cover AWS S3, MinIO, GCS, Backblaze B2, or any
S3-compatible tier. `accessKey` / `secretKey` are the bucket's own credentials
(omit them to use the service's ambient cloud credentials).

```ts
const { id: storageId } = await client.storages.create({
  accountId, regionId,
  name: 'prod-s3',
  storageType: 'object',
  providerType: 's3',
  endpoint: 'https://s3.us-east-1.amazonaws.com',
  bucket: 'mountos-data',
  region: 'us-east-1',
  // accessKey, secretKey  // optional bucket credentials
})
```

Validate reachability before or after creating it. `POST /api/v1/storages/test-bucket`
(ad hoc) or `POST /api/v1/storages/:storageId/test-bucket` returns
`{ bucketExists, list, write, read, delete, multipart }`. Treat a `false` on
`write` or `multipart` as a blocker; clients will fail byte I/O.

## 2. Create a volume

A volume lives in exactly one region and is served by exactly one cluster. By
default it lands on the region's default cluster (`uno`, unless the operator has
set another default); pass `regionClusterId` (or `regionClusterUuid`) to pin it
to a named cluster.

```ts
const { id: volumeId, encryptionKey } = await client.volumes.create({
  accountId, storageId,
  name: 'workspace',
  volumeType: 'general',   // general object volume
  // optional knobs below
  encryption: true,        // server returns the generated encryptionKey
  retentionPeriod: 30,     // days of history / recently-deleted (0-366, default 30)
  gracePeriod: 14,         // days after deactivate before reap (0-91, default 14)
  forkGracePeriod: 1,      // days a discarded fork lingers (0-30, default 1)
  quotaLimit: 10 * 1024 ** 3,
  // regionClusterId: <id> // non-default cluster
})
```

The response is `{ id, encryptionKey }`. `encryptionKey` is only meaningful when
`encryption: true`; with encryption off it is empty and can be ignored. When
encryption is on, capture it now (see the note below).

| Knob | Controls | Default | Range |
|------|----------|---------|-------|
| `encryption` | Per-volume envelope encryption; response carries `encryptionKey` | off | bool |
| `retentionPeriod` | How far time travel reaches; recently-deleted window; version/tombstone pruning | 30 | 0-366 days |
| `gracePeriod` | Delay after `deactivate` before gcserv reaps secrets + data | 14 | 0-91 days |
| `forkGracePeriod` | How long a discarded fork's data lingers | 1 | 0-30 days |
| `eventLogRetentionPeriod` | Change-event feed: gates capture and sets the readable window (0 = off, no rows accumulate) | 0 | 0-30 days |
| `quotaLimit` | Volume size ceiling (bytes) | unset (operator/account policy) | int64 |
| `regionClusterId` | Pin to a non-default cluster in the same region | `uno` | int64 |

Note: the embedded S3/WebHDFS gateway (`mountos gateway --gateway s3,hdfs`) refuses
lake/Iceberg volume types. Use a general object volume if S3/WebHDFS access is
needed; an Iceberg volume exposes its own lake catalog and S3 endpoint instead.

`retentionPeriod`, `gracePeriod`, `forkGracePeriod`, and `eventLogRetentionPeriod`
are editable later via `PUT /api/v1/volumes/:volumeId/edit`. The quota is not part
of `/edit`; change it only via `PUT /api/v1/volumes/:volumeId/quota { quotaLimit }`.

If `encryption` is set, capture the returned `encryptionKey` now; it is not
retrievable later.

## 3. Generate an S3-style access key

The access key pair (`apiKey` = access-key id, `apiSecret`) scopes a user to the
volume and encodes the volume's routing identity, which is what the HUB resolves
at discovery time. Generate one per user.

The SDK wraps this as `volumes.generateAPIKeys` (and `volumes.revokeAPIKey` /
`volumes.revokeAPIKeysByUser`). The REST path above works the same with the signed
JWT for older packages or other tooling.

```http
POST /api/v1/volumes/:volumeId/api-keys/generate
Authorization: Bearer <Ed25519 JWT>
{ "userId": <int64> }
-> { "apiKey": "<20 chars>", "apiSecret": "<40 chars>" }
```

```sh
# raw call with a pre-signed admin JWT in $MOUNTOS_JWT
curl -fsS -X POST "https://hub.example.com/api/v1/volumes/$VOLUME_ID/api-keys/generate" \
  -H "Authorization: Bearer $MOUNTOS_JWT" \
  -H 'Content-Type: application/json' \
  -d "{\"userId\": $USER_ID}"
```

Show `apiSecret` once. It is not stored in retrievable form. Revoke when needed:

```http
POST /api/v1/volumes/:volumeId/api-keys/revoke          { "apiKey": "<id>" }
POST /api/v1/volumes/:volumeId/api-keys/revoke-by-user  { "userId": <int64> }
```

The same key authenticates the filesystem mount, the S3 surface, and WebHDFS.

## 4. Install and mount the client

The client binary is `mountos`. Install it on the user's machine:

```sh
curl -fsSL https://download.mountos.sh | bash   # installs the mountos client
```

Mount the volume. Give it the HUB domain as the discovery URL plus the access
key pair; the client calls `GET /api/v1/discover/meta?accessKeyId=...` at the
HUB once (no JWT; the HUB caches resolutions for 15s and the client caches the
result on disk under `~/.mountOS/cache/`), learns the owning region and cluster,
then connects there for the session.

```sh
mountos mount \
  --mount /mnt/workspace \
  --discovery-url https://hub.example.com \
  --access-key-id <apiKey> \
  --secret-access-key <apiSecret>
```

| Flag | Env | Meaning |
|------|-----|---------|
| `--mount, -m` | `FUSE_MOUNT_POINT` | Mount point directory (required) |
| `--discovery-url` | `MOUNTOS_DISCOVERY_URL` | HUB HTTPS URL (required) |
| `--access-key-id, -a` | `MOUNTOS_ACCESS_KEY_ID` | Access-key id (20 chars) |
| `--secret-access-key, -s` | `MOUNTOS_SECRET_ACCESS_KEY` | Secret key (40 chars) |
| `--fork-name` | | Fork to mount (empty = main line) |
| `--mount-opts, -o` | | FUSE opts: `allow_other`, `ro`, `debug`, etc. |

For the `mountos` client, `--discovery-url` / `MOUNTOS_DISCOVERY_URL` is the
appserv HTTPS URL (the REST discovery call), not a raw `host:port`. The same env
name means a raw dataserv `host:port` for block/gateway services and for the CSI
`metaURL`; see provision.md's `MOUNTOS_DISCOVERY_URL differs by consumer` note so
you never point the wrong scheme at the wrong service. Both credential flags must
be supplied together, or both via env. macOS uses the mountOSX app; Windows uses
the native driver. The same volume is reachable over S3 and WebHDFS with no mount
using the same key (see the integrate skill).

## 5. The data features a user gets

These are built into every general volume, with no extra subsystem to operate.
All are bounded by the volume's `retentionPeriod`. For the conceptual depth
(retention floor, how forks pin history), read overview.md; the surface a user
drives is below.

- **Git-style forks.** Branch the whole volume in one call; no data is copied.
  The fork diverges only as new writes land. A fork can be forked (bounded to 8
  levels). Forks do not merge back. Mount a fork with `--fork-name <name>`, or
  manage forks from the client (`mountos fork`) or the API
  (`POST /api/v1/volumes/:volumeId/forks/create`, optionally `asOf` a timestamp
  to branch from a past point). Temporary forks live only for a mount session and
  never write file data to the backend.
- **Time travel / point-in-time.** Open the volume as it looked at any timestamp
  within retention. `mountos snapshot` gives a read-only point-in-time mount;
  the API exposes it via `asOf` on the fork tree/entry endpoints.
- **One-minute version history.** Every file keeps a rolling history at
  one-minute granularity. Multiple saves inside a minute collapse to the last.
  List, diff, or restore versions per file; the API is
  `GET /api/v1/volumes/:volumeId/forks/:forkName/entry/versions`.
- **Recently-deleted restore.** Deleted files, folders, and subtrees stay
  restorable for the retention window. Restores are instant and metadata-only.
  `mountos deleted` mounts a view showing only deleted entries.
- **Read-only snapshot / deleted-only mounts.** `mountos snapshot` (point-in-time,
  read-only) and `mountos deleted` (deleted entries only) are dedicated mount
  modes alongside the normal read-write mount.
- **Change-event feed.** With `eventLogRetentionPeriod` > 0, the volume keeps an
  ordered feed of creates, deletes, modifies, and renames (per fork, monotonic
  `seq`). `mountos event -a <key> -s` streams it (`--since 7d`, `--follow`,
  `--path-prefix`, `--json`), or `--http` serves it as a local REST API. General
  volumes only; bounded by its own knob, not `retentionPeriod`. Full contract:
  https://mountos.io/ai/topics/change-events.md.

## 6. Operate the volume

- Pin or relocate load: `POST /api/v1/volumes/:volumeId/move-cluster`
  `{ targetClusterId? }` returns `{ handoverUntil }` (a transparent handover; the
  client never reconfigures). Add a cluster first with
  `POST /api/v1/regions/:regionId/clusters/create { name }` if needed. These are
  REST-only; do not look for an SDK wrapper. See operate.md for day-2 scaling.
- Freeze writes: `POST /api/v1/volumes/:volumeId/lock` and `.../unlock`.
- Retire: `POST /api/v1/volumes/:volumeId/deactivate` (optional cleanup flags),
  reversible with `.../activate` until `gracePeriod` elapses.
- Inspect: `GET /api/v1/volumes/:volumeId/stats` and `.../size-history`.

When unsure of a method name, use the REST path directly; do not guess SDK
wrappers. Full reference: `mountos-admin-sdk/api.md`.

## Read next

- Index: https://mountos.io/skill.md
- Stand up the HUB and a region first: https://mountos.io/skills/provision.md
- Reach the same volume over S3, WebHDFS, and Kubernetes CSI: https://mountos.io/skills/integrate.md
- Day-2 ops, scaling, and cluster moves: https://mountos.io/skills/operate.md
- Headline data features in depth: https://mountos.io/ai/topics/overview.md
- Client flags and discovery wiring: https://mountos.io/ai/topics/configuration.md
- Full guide: https://mountos.io/ai/topics/getting-started.md
- Downloads and install: https://download.mountos.sh
- Support: support@mountos.io
