diff --git a/README.md b/README.md
index 464f9d7..5e8999f 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,52 @@
-# mökv
+
+
+

+
+
-`mökv` is a distributed, in-memory key-value store built with [`Raft`](https://github.com/hashicorp/raft) for consensus, [`Serf`](https://github.com/hashicorp/serf) for discovery, and [`gRPC`](https://github.com/grpc/grpc-go) for communication.
+[](https://opensource.org/licenses/MIT)
+[](https://github.com/dynamic-calm/mokv/actions/workflows/test.yml)
-Built it following the book [Distributed Services with Go](https://pragprog.com/titles/tjgo/distributed-services-with-go/) by Travis Jeffery.
+
+
+
+
+## Example
+
+
+
+```go
+package main
+
+import (
+ "context"
+
+ "github.com/dynamic-calm/mokv/mokv"
+)
+
+func main() {
+ client, _ := mokv.NewClient("localhost:8400")
+ defer client.Close()
+
+ ctx := context.Background()
+
+ key := `59°19'14.7"N`
+ val := []byte(`18°03'39.0"E`)
+
+ client.Set(ctx, key, val)
+ client.Get(ctx, key)
+ client.Delete(ctx, key)
+ client.GetServers(ctx)
+}
+```
+
+
+
+## Overview
+
+mökv is a distributed key-value store built with [Raft](https://github.com/hashicorp/raft) for consensus, [Serf](https://github.com/hashicorp/serf) for discovery, and [gRPC](https://github.com/grpc/grpc-go) for communication.
+
+Built following the book [Distributed Services with Go](https://pragprog.com/titles/tjgo/distributed-services-with-go/) by Travis Jeffery.
## Features
diff --git a/mokv.png b/mokv.png
new file mode 100644
index 0000000..7101c0f
Binary files /dev/null and b/mokv.png differ
diff --git a/mokv/client.go b/mokv/client.go
new file mode 100644
index 0000000..9f97d89
--- /dev/null
+++ b/mokv/client.go
@@ -0,0 +1,75 @@
+package mokv
+
+import (
+ "context"
+
+ "github.com/dynamic-calm/mokv/api"
+ "google.golang.org/grpc"
+ "google.golang.org/grpc/credentials/insecure"
+ "google.golang.org/protobuf/types/known/emptypb"
+)
+
+// Client provides access to a mökv cluster.
+type Client struct {
+ conn *grpc.ClientConn
+ api api.KVClient
+}
+
+// Server represents a node in the cluster.
+type Server struct {
+ RpcAddr string
+ IsLeader bool
+ ID string
+}
+
+// NewClient connects to a mökv node at the given address.
+func NewClient(addr string) (*Client, error) {
+ conn, err := grpc.NewClient(addr,
+ grpc.WithTransportCredentials(insecure.NewCredentials()))
+ if err != nil {
+ return nil, err
+ }
+ return &Client{
+ conn: conn,
+ api: api.NewKVClient(conn),
+ }, nil
+}
+
+// Get retrieves a value by key. Returns nil if the key does not exist.
+func (c *Client) Get(ctx context.Context, key string) ([]byte, error) {
+ res, err := c.api.Get(ctx, &api.GetRequest{Key: key})
+ if err != nil {
+ return nil, err
+ }
+ return res.Value, nil
+}
+
+// Set stores a key-value pair, replicating it across the cluster.
+func (c *Client) Set(ctx context.Context, key string, value []byte) (bool, error) {
+ res, err := c.api.Set(ctx, &api.SetRequest{Key: key, Value: value})
+ return res.Ok, err
+}
+
+// Delete removes a key from the cluster.
+func (c *Client) Delete(ctx context.Context, key string) (bool, error) {
+ res, err := c.api.Delete(ctx, &api.DeleteRequest{Key: key})
+ return res.Ok, err
+}
+
+// GetServers returns all nodes in the cluster.
+func (c *Client) GetServers(ctx context.Context) ([]Server, error) {
+ res, err := c.api.GetServers(ctx, &emptypb.Empty{})
+ if err != nil {
+ return nil, err
+ }
+ servers := make([]Server, len(res.Servers))
+ for i, s := range res.Servers {
+ servers[i] = Server{RpcAddr: s.RpcAddr, IsLeader: s.IsLeader, ID: s.Id}
+ }
+ return servers, nil
+}
+
+// Close releases the underlying connection.
+func (c *Client) Close() error {
+ return c.conn.Close()
+}