# AtlasKV

AtlasKV is a distributed key-value store designed for small clusters that need strong consistency, predictable failover, and a simple HTTP API. Data is replicated using the Raft consensus algorithm, and writes are acknowledged only after they have been committed by a majority of nodes.

AtlasKV is suitable for configuration data, service coordination, metadata, and other workloads where correctness matters more than raw write throughput.

## Features

- Strongly consistent reads and writes
- Raft-based leader election and replication
- Automatic failover when a leader becomes unavailable
- Persistent storage with write-ahead logging
- Time-to-live (TTL) support
- Compare-and-swap operations
- Prefix scans with pagination
- HTTP health and metrics endpoints
- Graceful cluster membership changes
- Optional TLS and token authentication
- Prometheus-compatible metrics

## Installation

### Prebuilt binaries

Download the archive for your platform from the project releases page, then extract it:

```sh
tar -xzf atlaskv_0.8.0_linux_amd64.tar.gz
sudo install atlaskv /usr/local/bin/atlaskv
```

Verify the installation:

```sh
atlaskv version
```

### Docker

```sh
docker pull ghcr.io/example/atlaskv:0.8.0
```

### Build from source

AtlasKV requires Go 1.23 or later.

```sh
git clone https://github.com/example/atlaskv.git
cd atlaskv
go build -o bin/atlaskv ./cmd/atlaskv
```

## Usage

### Start a single-node cluster

A single-node cluster is useful for development and local testing:

```sh
atlaskv server \
  --node-id node-1 \
  --data-dir ./data/node-1 \
  --client-addr 127.0.0.1:7400 \
  --peer-addr 127.0.0.1:7500 \
  --bootstrap
```

The client API is now available at `http://127.0.0.1:7400`.

### Start a three-node cluster

Start the first node and bootstrap the cluster:

```sh
atlaskv server \
  --node-id node-1 \
  --data-dir ./data/node-1 \
  --client-addr 127.0.0.1:7401 \
  --peer-addr 127.0.0.1:7501 \
  --advertise-peer-addr 127.0.0.1:7501 \
  --bootstrap
```

Join two additional nodes:

```sh
atlaskv server \
  --node-id node-2 \
  --data-dir ./data/node-2 \
  --client-addr 127.0.0.1:7402 \
  --peer-addr 127.0.0.1:7502 \
  --advertise-peer-addr 127.0.0.1:7502 \
  --join 127.0.0.1:7401
```

```sh
atlaskv server \
  --node-id node-3 \
  --data-dir ./data/node-3 \
  --client-addr 127.0.0.1:7403 \
  --peer-addr 127.0.0.1:7503 \
  --advertise-peer-addr 127.0.0.1:7503 \
  --join 127.0.0.1:7401
```

Use `--bootstrap` only when creating a new cluster. Restarting an existing node automatically restores its cluster state from the data directory.

### Write a value

```sh
curl -X PUT http://127.0.0.1:7401/v1/kv/app/config \
  -H 'Content-Type: application/json' \
  -d '{"value":"production"}'
```

Example response:

```json
{
  "key": "app/config",
  "revision": 42
}
```

### Read a value

```sh
curl http://127.0.0.1:7401/v1/kv/app/config
```

```json
{
  "key": "app/config",
  "value": "production",
  "revision": 42,
  "expires_at": null
}
```

### Set a TTL

The TTL is specified in seconds:

```sh
curl -X PUT http://127.0.0.1:7401/v1/kv/sessions/abc123 \
  -H 'Content-Type: application/json' \
  -d '{"value":"user-17","ttl":3600}'
```

### Compare and swap

Update a key only if its current revision matches the expected revision:

```sh
curl -X PUT http://127.0.0.1:7401/v1/kv/app/config \
  -H 'Content-Type: application/json' \
  -H 'If-Match: 42' \
  -d '{"value":"maintenance"}'
```

A revision mismatch returns `409 Conflict`.

### List keys by prefix

```sh
curl 'http://127.0.0.1:7401/v1/kv?prefix=app/&limit=100'
```

### Delete a key

```sh
curl -X DELETE http://127.0.0.1:7401/v1/kv/app/config
```

### Check cluster status

```sh
curl http://127.0.0.1:7401/v1/cluster/status
```

### Health and metrics

```sh
curl http://127.0.0.1:7401/health
curl http://127.0.0.1:7401/ready
curl http://127.0.0.1:7401/metrics
```

`/health` reports whether the process is running. `/ready` returns success only when the node can serve requests or forward them to the current leader.

## Configuration

AtlasKV accepts command-line flags or a YAML configuration file:

```sh
atlaskv server --config /etc/atlaskv/config.yaml
```

Command-line flags override values from the configuration file.

Example configuration:

```yaml
node:
  id: node-1
  data_dir: /var/lib/atlaskv

network:
  client_addr: 0.0.0.0:7400
  peer_addr: 0.0.0.0:7500
  advertise_client_addr: atlaskv-1.internal:7400
  advertise_peer_addr: atlaskv-1.internal:7500

cluster:
  bootstrap: false
  join:
    - atlaskv-1.internal:7400
    - atlaskv-2.internal:7400
  election_timeout: 1500ms
  heartbeat_interval: 250ms
  snapshot_interval: 10000
  snapshot_retention: 3

storage:
  sync_writes: true
  max_value_size: 1048576
  compaction_interval: 10m

api:
  request_timeout: 5s
  max_request_body: 2097152
  allow_stale_reads: false

security:
  auth_token_file: /etc/atlaskv/token
  tls:
    enabled: true
    certificate: /etc/atlaskv/tls/server.crt
    private_key: /etc/atlaskv/tls/server.key
    client_ca: /etc/atlaskv/tls/ca.crt
    require_client_certificate: true

logging:
  level: info
  format: json
```

### Important settings

| Setting | Default | Description |
| --- | --- | --- |
| `node.id` | Required | Unique, stable identifier for the node. |
| `node.data_dir` | `./data` | Directory containing logs, snapshots, and metadata. |
| `network.client_addr` | `127.0.0.1:7400` | Address used by clients and administrative requests. |
| `network.peer_addr` | `127.0.0.1:7500` | Address used for Raft traffic between nodes. |
| `cluster.election_timeout` | `1500ms` | Time without leader contact before starting an election. |
| `cluster.heartbeat_interval` | `250ms` | Interval between leader heartbeats. |
| `cluster.snapshot_interval` | `10000` | Number of applied log entries between snapshots. |
| `storage.sync_writes` | `true` | Flush committed writes to disk before acknowledging them. |
| `storage.max_value_size` | `1048576` | Maximum value size in bytes. |
| `api.allow_stale_reads` | `false` | Allow followers to serve potentially stale reads. |
| `security.auth_token_file` | Empty | File containing the bearer token required by the API. |
| `logging.level` | `info` | Log level: `debug`, `info`, `warn`, or `error`. |

### Authentication

When `security.auth_token_file` is configured, include the token with each request:

```sh
curl http://127.0.0.1:7400/v1/kv/app/config \
  -H "Authorization: Bearer ${ATLASKV_TOKEN}"
```

The token file should be readable only by the AtlasKV process:

```sh
chmod 600 /etc/atlaskv/token
```

### Operational notes

- Run an odd number of voting nodes, typically three or five.
- A majority of nodes must be available for writes and strongly consistent reads.
- Store each node's data directory on durable local storage.
- Do not copy a data directory between nodes; each node has persistent identity and consensus state.
- Back up snapshots together with the cluster metadata generated by `atlaskv backup`.
- Test membership changes and restores in a non-production environment before relying on them operationally.