---
title: "Developer Guide"
---

```mermaid
flowchart LR
    Stack[Run Local Stack] --> SDK[Install SDK]
    SDK --> App[Build Your App]
    App --> Features[Add Social Features]
    Features --> Production[Production Setup]
```

This guide walks you through building a first Pubky app against a local development stack. You will start a local Homeserver with Pubky Docker, create a Vite app, install the Pubky SDK, and connect your app to the local testnet.

By the end, you will have created a demo identity, signed up and signed in on the local Homeserver, written a JSON file to Pubky storage, and read it back in the browser. After that, you will also get to know the templates you can use to bootstrap your own Pubky app.

To follow along, you will need [Docker](https://docs.docker.com/get-started/get-docker/) and [npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm/).

### Step 1: Set Up Pubky Docker

:::note[Prefer a native setup?]
If you do not want to use Docker, see the [native Pubky testnet setup](https://github.com/pubky/pubky-homeserver/blob/v0.14.0/pubky-testnet/README.md).
:::

In order to build our App we'll need to setup a local homeserver and testnet - we'll use [Pubky Docker](https://pubky.org/pubky-docker.md) to spin up a local development environment.

Note: [Pubky Docker](https://pubky.org/pubky-docker.md)  can run a full [pubky.app](https://pubky.org/pubky-app.md)-compatible social stack too, but we will keep this setup minimal.

```bash
git clone https://github.com/pubky/pubky-docker.git && cd pubky-docker && cp .env-sample .env
```

Make sure your Docker engine is running. On Linux, [start the daemon](https://docs.docker.com/engine/daemon/start/); with Docker Desktop, open the app. For [Colima](https://colima.run/docs/getting-started/), run:

```bash
colima start
```

Run the homeserver and testnet via Docker compose:

```bash
docker compose up homeserver -d
```

Open [http://localhost:15411/](http://localhost:15411/) (PKARR relay) and [http://localhost:6288/](http://localhost:6288/) (Homeserver admin) in your browser to verify they respond.

You now have a local Pubky testnet ready for app development. An isolated DHT is running, the HTTP relay is local, and the Homeserver publishes its PKARR identity to the local DHT. This means local clients can discover your Homeserver the same way they would on the public network, with local testnet services. The Docker stack publishes ports and uses development credentials; run it only on an isolated development machine or network, with disposable data and identities. Your testnet Homeserver's pubky is always `8pinxxgqs41n4aididenw5apqp1urfmzdztr8jt4abrkdn435ewo`.

:::note[Testnet state is ephemeral]
When the Docker containers are restarted the files stored on the Homeserver and user PKARR records in the local DHT are reset. The testnet Homeserver does however have a stable, predefined pubky.
:::

With `.env` set to the default `NETWORK=testnet`, these ports are exposed:

| Port | Service | Purpose |
| --- | --- | --- |
| `15411` | [PKARR](https://pubky.org/pkarr.md) relay | Used by the Pubky SDK to publish and resolve testnet PKARR records over HTTP, instead of using the [Mainline DHT](https://pubky.org/mainline-dht.md). |
| `15412` | [HTTP relay](https://pubky.org/http-relay.md) | Runs the local relay used by Pubky authentication flows. |
| `6286` | Homeserver ICANN HTTP | Clear-text HTTP endpoint used for browser and localhost fallback. |
| `6287` | Homeserver [PubkyTLS](https://pubky.org/glossary.md#pubkytls) | Direct Pubky TLS endpoint for SDK and native clients. |
| `6288` | Homeserver admin HTTP | Local admin endpoint exposed by Pubky Docker. |

:::note[CLI examples]
For manual user and Homeserver operations while developing locally, you can use [JavaScript CLI examples](https://github.com/pubky/pubky-homeserver/tree/v0.14.0/examples/javascript).
:::

For source builds, see [Optional: Build from source](https://github.com/pubky/pubky-docker/blob/main/Readme.md).

### Step 2: Initialize Project with the SDK

What follows is a step-by-step guide to building your first Pubky app. If you prefer to start from a ready-made project, jump to the [basic Pubky app template](#39-basic-pubky-app-template).

:::note[Reference docs]
For full API details see the reference documentation for [JavaScript](https://pubky.github.io/pubky-homeserver/js-sdk-typedoc/) and [Rust](https://docs.rs/pubky).
:::

With the Homeserver running, clone this empty Vite template and install the [Pubky SDK](https://pubky.org/sdk.md):

```bash
npx tiged pubky/pubky-app-templates/vite-starter pubky-hello-world
cd pubky-hello-world
npm install && npm install @synonymdev/pubky
```

NPM package: [@synonymdev/pubky](https://www.npmjs.com/package/@synonymdev/pubky)

<details>
<summary><strong>Other tools and platforms</strong></summary>

If you are using another language, package manager, or framework, install the SDK like this. Dedicated guides for these will follow.

**Yarn:**
```bash
yarn add @synonymdev/pubky
```

**Rust ([docs](https://docs.rs/pubky)):**
```bash
cargo add pubky
```

**React Native:**
```bash
npm install @synonymdev/react-native-pubky
cd ios && pod install  # iOS only
```

**iOS/Android Native**: See [SDK Documentation](https://pubky.org/sdk.md) for UniFFI bindings via `pubky-core-ffi`.

</details>

### Step 3: Build Your First App

Open `src/main.ts` and replace the `document.querySelector('#app')!.textContent = 'Vite Starter'` line with the snippets below.

#### 3.1 Import the SDK and enable info logs

```js
import { Keypair, Pubky, PublicKey, setLogLevel } from "@synonymdev/pubky";

try {
  setLogLevel("info");
} catch (error) {
  console.warn(
    "Pubky log level must be set only once, before creating the client.",
    error,
  );
}
```

This loads the Pubky SDK and sends info logs to the browser console.

To see the logs in your browser console, make sure you have the right log level filtering configured in your browser as well.

#### 3.2 Connect to the local testnet

```js
const pubky = Pubky.testnet();
```

This tells the SDK to use the local testnet services started by Pubky Docker instead of the production Pubky network.

#### 3.3 Create a new user identity

```js
const keypair = Keypair.random();
const signer = pubky.signer(keypair);
console.log("Your pubky:", signer.publicKey.z32());
```

This creates a demo identity for the hello-world app and logs its pubky to the browser console.

#### 3.4 Sign up on the local Homeserver

```js
const homeserver = PublicKey.from(
  "pubky8pinxxgqs41n4aididenw5apqp1urfmzdztr8jt4abrkdn435ewo",
);

await signer.signup(homeserver, null);
```

This creates an account on the local Homeserver and publishes the user's Homeserver mapping (PKARR). Because [local signup is set to `open`](https://github.com/pubky/pubky-docker/blob/75b1121f3e90b9b44d9416ca4f5ba87a4984e800/homeserver.config.toml#L5), we pass `null` instead of a signup token.

:::note[Homeserver signup]
This guide performs Homeserver signup inside the app because it is the shortest path to a working local example. In a real-world flow, however, Homeserver signup is not the responsibility of a Pubky app. Assume users already have an account on a Homeserver. If not, direct them to a separate signup flow, such as [the onboarding on pubky.app](https://pubky.app/onboarding/human), instead of implementing it in the app. The template in Step 3.9 follows this pattern.
:::

Run `npm run dev` and open the printed URL in your browser. Look at the logs in your browser console. You should see that the signup request succeeded and that you successfully published your Homeserver configuration (= PKARR).

:::note[404 during signup]
During first signup, the browser console may show a `404` for a request to `http://localhost:15411/<user-public-key>`. That can be normal: the SDK checks whether the new user's PKARR record exists before publishing it. If signup continues, you can ignore that `404`.
:::

#### 3.5 Sign in

```js
const session = await signer.signin("myapp.example");
```

This creates a Homeserver session for the demo user.

#### 3.6 Write to Homeserver storage

```js
const path = "/pub/hello-world/data.json";
await session.storage.putJson(path, { message: "Hello Pubkyverse!" });
```

This writes a simple JSON file onto the signed-in user's Homeserver public storage.

:::tip[Store independent records separately]
Store each independently edited record in its own file. For example, a posts collection can use `/pub/myapp/posts/001.json` and `/pub/myapp/posts/002.json`. A write replaces the data at one path, so this layout lets your app:

- **Send smaller updates:** Upload the changed record without rewriting unchanged data.
- **Load records as needed:** Page through the directory listing and fetch only the files needed for the current view.
- **Reduce potential write conflicts:** Editing the same file from different processes or apps needs coordination.

That said, keep fields together when you read and update them as a unit. Splitting them adds unnecessary requests.
:::

#### 3.7 Read the JSON back

```js
const data = await session.storage.getJson(path);
document.querySelector<HTMLDivElement>("#app")!.textContent = JSON.stringify(
  data,
  null,
  2,
);
```

This fetches the same JSON file from Homeserver storage and renders it in the template's `#app` element, proving that signup, signin, write, and read all worked.

Run `npm run dev` again and open the page. You should now see the data displayed there.

#### 3.8 Inspect Homeserver data

Open the [testnet Pubky Explorer](https://explorer.pubky.app/testnet/), enter the pubky logged in Step 3.3, and browse to `/pub/hello-world/data.json`.

The hosted app connects to `localhost`; allow local-network access if prompted. Alternatively, clone [Pubky Explorer](https://github.com/pubky/pubky-explorer) and run it locally.

:::tip[First app complete]
Nice. Your first Pubky app works.
:::

#### 3.9 Basic Pubky app template

As a next step, try [this template](https://pubky.github.io/pubky-app-templates/) as a fuller starting point for a fresh Pubky app.

It includes a working browser app with local testnet configuration, identity creation, Homeserver signup and signin, and a Pubky auth flow. Treat it as a set of building blocks: copy the pieces your app needs, adapt the auth and storage flows, and replace the sample UI with your own experience.

```bash
npx tiged pubky/pubky-app-templates/basic-pubky-app my-pubky-app
cd my-pubky-app
npm install
```

Set a stable app ID in `src/config.ts`; it determines the storage path.

```bash
VITE_PUBKY_TESTNET=true npm run dev
```

##### Homeserver auth

The basic app offers two login paths. The right-side **New identity** panel is a development shortcut: it creates a keypair inside the app, signs up with the Homeserver, and signs in. The left-side **Sign in with Pubky Ring** panel shows the recommended authentication flow: the app initiates sign-in, while responsibility for key management and Homeserver signup remains with components outside the app.

For local development, you can use the [Pubky Ring Simulator](https://simulator.pubkyring.app/) as a browser-based stand-in for Pubky Ring.

The simulator is preconfigured to connect to your local testnet on `localhost`. Because the hosted version accesses services running on your device, your browser may ask whether `simulator.pubkyring.app` can access apps and services on your device or devices on your local network. Choose **Allow** to continue. If you prefer not to grant this permission, clone the simulator and follow its [development instructions](https://github.com/pubky/pubky-ring-simulator#development) to run it locally.

1. In `my-pubky-app`, use the left-side **Sign in with Pubky Ring** panel and click **Copy link**.
2. In the simulator, select **Shortcut** mode and paste the link into **Auth link**.
3. The simulator creates an identity, signs it up on your local Homeserver, then approves the request automatically.
4. Switch back to `my-pubky-app`. It polls the pending auth flow and signs in automatically once the simulator approves.
5. To test sign-in with different identities, use the Pubky Ring Simulator in **Regular** mode. It simulates Pubky Ring's identity management, letting you create and select an identity before authorizing a request.

That flow is the security best practice: it keeps the user's key material out of the application being tested. The Pubky app template receives a session after authorization, but the identity and Homeserver setup remain with the signer.

### Step 4: Add Social Features

:::note[Guide coming soon]
For now, this section collects references. A dedicated guide will follow.
:::

**Learn from working examples:**
- [mypubky.com](https://mypubky.com/) ([source](https://github.com/pubky/mypubky))
- [eventky.app](https://eventky.app/) ([source](https://github.com/gillohner/eventky))
- [mapky.app](https://mapky.app/) ([source](https://github.com/gillohner/mapky-app))

**Social App (pubky-app-specs):**
- [pubky-app-specs](https://github.com/pubky/pubky-app-specs) - Data models for social features and interoperability with [pubky.app](https://pubky.org/pubky-app.md)
- [npm: pubky-app-specs](https://www.npmjs.com/package/pubky-app-specs) / [crates.io: pubky-app-specs](https://crates.io/crates/pubky-app-specs)

**Use Pubky Nexus for Social Features:**

If building a social app, leverage [Pubky Nexus](https://pubky.org/pubky-nexus.md) for:
- Real-time feeds and timelines
- Search and discovery
- User recommendations
- Notifications

```javascript
const response = await fetch(
  "https://nexus.pubky.app/v0/stream/posts?limit=10",
);
const posts = await response.json();
```

📊 [Nexus API Docs](https://nexus.pubky.app/swagger-ui/)

**Add Payments (WIP):**

[Paykit](https://pubky.org/paykit.md) protocol (work in progress) will enable:
- Payment discovery via Pubky public keys
- Public or private payment details for Bitcoin onchain, Lightning, and other rails
- Encrypted receipt access for payers
- Subscriptions and payment request workflows

**Add Encryption (WIP):**

[Pubky Noise](https://pubky.org/pubky-noise.md) (work in progress) provides:
- Encrypted peer-to-peer channels
- Private messaging
- Secure data sharing

### Step 5: Production Setup

To connect your app to the production Pubky network, replace the client from Step 3.2:

```diff
- const pubky = Pubky.testnet();
+ const pubky = new Pubky();
```

`new Pubky()` stops using the local endpoints. The app instead resolves [PKARR](https://pubky.org/pkarr.md) records from the [Mainline DHT](https://pubky.org/mainline-dht.md), connects to the Homeserver resolved from each user's PKARR record, and uses a public [HTTP relay](https://pubky.org/http-relay.md) for authentication.

Steps 3.3–3.5 use development-only identity and Homeserver shortcuts. For production, use [Pubky Ring](https://pubky.org/pubky-ring.md); the [basic Pubky app template](#39-basic-pubky-app-template) already implements that flow.

<details>
<summary><strong>Optional: Configure custom relays</strong></summary>

Browsers cannot query the UDP-based Mainline DHT directly, so the SDK uses HTTPS gateways called **PKARR relays**. See the [current default relay list](https://github.com/pubky/pkarr/blob/main/pkarr/src/lib.rs). To use custom PKARR relays:

```javascript
const client = new Client({
  pkarr: {
    relays: ["https://pkarr.pubky.org"],
  },
});

const pubky = Pubky.withClient(client);
```

PKARR relays are separate from the [HTTP relay](https://pubky.org/http-relay.md) that transfers encrypted Pubky Ring authentication messages. To use a custom HTTP relay with the SDK:

```javascript
import { AuthFlowKind } from "@synonymdev/pubky";

const relay = "https://httprelay.example.com/inbox/";
const flow = await pubky.startGrantAuthFlow(
  "/pub/myapp/:rw",
  AuthFlowKind.signin(),
  {
    clientId: "myapp.example",
    relay,
  },
);
```

The basic template maps [`VITE_PUBKY_HTTP_RELAY`](https://github.com/pubky/pubky-app-templates/blob/main/basic-pubky-app/src/config.ts) to the same SDK option.

</details>

#### Test the Setup

1. Start the production-configured app and authenticate through Pubky Ring with your production identity.
2. Use the app to create some sample data.
3. Enter your pubky in [Pubky Explorer](https://explorer.pubky.app) and verify the files created by the app.

### Next Steps

- **Explore SDK examples:** See the [Pubky Homeserver examples](https://github.com/pubky/pubky-homeserver/tree/v0.14.0/examples) for runnable workflows.
- **Find SDK references:** See the [Pubky SDK guide](https://pubky.org/sdk.md) for supported platforms, API references, and examples.
- **Choose an app architecture:** Compare [client-only, aggregator, and custom-backend designs](https://pubky.org/app-architectures.md).
- **Security model:** Review the [security considerations for app developers](https://pubky.org/security-model.md).

Need help? Ask on pubky.app or [Telegram](https://t.me/pubkycore).
