# Agora

Power to the people.

Agora is a Nostr client focused on community ownership, expressive identity, and censorship resistance. This repository (`agora-3`) is the Agora-branded app built from the Ditto codebase.

**[agora.spot](https://agora.spot)** | **[Source](https://gitlab.com/soapbox-pub/agora-3)**

## What This Repo Is

- Agora product identity (name, theme, assets, native IDs)
- Ditto-derived implementation with broad Nostr feature coverage
- Configurable deployment defaults via `agora.json`

## Features

- **Community-first social client**: notes, articles, comments, reposts, reactions, and rich event rendering
- **Theming system**: built-in presets + custom color/font/background themes that can be shared as events
- **Lightning support**: zaps with Nostr Wallet Connect and WebLN
- **Private messaging**: NIP-04 and NIP-17 direct messages
- **Mobile app shell**: Capacitor-powered Android/iOS wrappers
- **Self-hostable**: static web build + configurable relay and upload infrastructure

## Getting Started

### Prerequisites

- [Node.js](https://nodejs.org/) 22+
- npm 10.9.4+

### Development

```sh
git clone https://gitlab.com/soapbox-pub/agora-3.git
cd agora-3
npm install
npm run dev
```

Development server: `http://localhost:8080`

### Docker Getting Started

Use Docker Compose when you want the nginx reverse-proxy stack (necessary if you want decryptable media in messages - kind 15s of NIP 17):

```sh
git clone https://gitlab.com/soapbox-pub/agora-3.git
cd agora-3
cp .env.example .env
docker compose up --build
```

Proxy URL: `http://localhost:8083`

This starts:

- `vite` service on the internal Docker network (`vite:8080`)
- `web` service (`nginx`) on host port `8082`, proxying to Vite with websocket support

Stop stack:

```sh
docker compose down
```

Production-style container build:

```sh
docker compose -f docker-compose.prod.yml up --build
```

### Build

```sh
npm run build
```

Build output: `dist/`

### Validate

```sh
npm test
```

This runs type-checking, linting, unit tests, and production build checks.

## Configuration

Build-time config is read from `agora.json` (gitignored by default so each deployment can provide its own values).

```jsonc
{
  "theme": "dark",
  "relayMetadata": {
    "relays": [
      { "url": "wss://relay.ditto.pub", "read": true, "write": true },
      { "url": "wss://relay.primal.net", "read": true, "write": true },
      { "url": "wss://relay.damus.io", "read": true, "write": true }
    ]
  },
  "blossomServers": [
    "https://blossom.ditto.pub",
    "https://blossom.primal.net/"
  ]
}
```

Configuration priority (highest first):

1. User settings (local storage)
2. Build config (`agora.json`)
3. Hardcoded app defaults

Use a custom config path:

```sh
CONFIG_FILE=./my-config.json npm run build
```

## Deployment

Agora builds to static files and can be deployed to any static host.

- GitLab/GitHub Pages
- Netlify/Vercel
- VPS or any web server with SPA routing fallback

For Android:

```sh
npm run build
npx cap sync
npx cap open android
```

## Tech Stack

| Layer | Technology |
| --- | --- |
| Framework | React 18 |
| Build | Vite |
| Language | TypeScript |
| Styling | TailwindCSS 3 + shadcn/ui |
| Routing | React Router |
| Data | TanStack Query |
| Nostr | Nostrify + nostr-tools |
| Mobile | Capacitor |
| Testing | Vitest + React Testing Library |

## Contributing

Read [CONTRIBUTING.md](CONTRIBUTING.md) before opening a merge request.

## License

[AGPL-3.0](LICENSE)
