Skip to content

Commit f41bbee

Browse files
authored
Merge pull request #7 from selfmd/feat/dashboard-v2-handoff
feat(dashboard): add v2 dashboard handoff
2 parents 7f087f0 + 17e07e6 commit f41bbee

26 files changed

Lines changed: 978 additions & 979 deletions

.dockerignore

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
.git
2+
.github
3+
node_modules
4+
**/node_modules
5+
**/dist
6+
.DS_Store
7+
.env
8+
.env.*
9+
!.env.example
10+
coverage
11+
.tmp
12+
tmp

.env.example

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Dashboard/server
2+
PORT=3001
3+
HOST=127.0.0.1
4+
AGENT_NAME=network-selfmd-dashboard
5+
L2S_DATA_DIR=/data
6+
7+
# For Docker images, keep the image default HOST=0.0.0.0 inside the container,
8+
# but bind published ports to 127.0.0.1 on the host unless access is protected.
9+
# Do not expose the dashboard directly to the public internet.
10+
# Put it behind VPN, Tailscale, Cloudflare Access, or another auth layer.

.github/workflows/ci.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: CI
2+
3+
on:
4+
pull_request:
5+
push:
6+
branches: [main]
7+
8+
jobs:
9+
build-test:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: actions/checkout@v4
13+
- uses: actions/setup-node@v4
14+
with:
15+
node-version: 22
16+
- name: Enable pnpm
17+
run: corepack enable
18+
- name: Install native build dependencies
19+
run: sudo apt-get update && sudo apt-get install -y python3 make g++
20+
- name: Install dependencies
21+
run: pnpm install --frozen-lockfile
22+
- name: Build
23+
run: pnpm -r build
24+
- name: Test
25+
run: pnpm -r test
26+
27+
docker-build:
28+
runs-on: ubuntu-latest
29+
steps:
30+
- uses: actions/checkout@v4
31+
- name: Build dashboard image
32+
run: docker build -t networkselfmd-dashboard:ci .

Dockerfile

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
FROM node:22-bookworm-slim
2+
3+
WORKDIR /app
4+
5+
RUN apt-get update && apt-get install -y --no-install-recommends python3 make g++ ca-certificates && rm -rf /var/lib/apt/lists/* && corepack enable
6+
7+
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml tsconfig.json ./
8+
COPY packages ./packages
9+
10+
RUN pnpm install --frozen-lockfile && pnpm -r build && CI=true pnpm prune --prod
11+
12+
ENV NODE_ENV=production PORT=3001 HOST=0.0.0.0 L2S_DATA_DIR=/data AGENT_NAME=network-selfmd-dashboard
13+
14+
RUN mkdir -p /data && chown -R node:node /data /app
15+
USER node
16+
17+
EXPOSE 3001
18+
VOLUME ["/data"]
19+
20+
HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 CMD node -e "fetch('http://127.0.0.1:' + (process.env.PORT || 3001) + '/healthz').then(r => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))"
21+
22+
CMD ["node", "packages/dashboard/dist/server/index.js"]

docs/deployment.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Deployment
2+
3+
The dashboard is a local network agent with a browser UI. Treat it like an operator console, not a public SaaS page.
4+
5+
## Safety rule
6+
7+
Do **not** expose the dashboard directly to the public internet. It can show local node identity, peers, states, and decrypted room messages for states this node has joined.
8+
9+
Put it behind one of:
10+
11+
- Tailscale / WireGuard / private network
12+
- Cloudflare Access
13+
- reverse proxy with authentication
14+
- an internal-only host
15+
16+
## Docker
17+
18+
```bash
19+
docker build -t networkselfmd-dashboard .
20+
21+
docker run --rm \
22+
-p 127.0.0.1:3001:3001 \
23+
-v networkselfmd-data:/data \
24+
networkselfmd-dashboard
25+
```
26+
27+
Open `http://127.0.0.1:3001`.
28+
29+
## Environment
30+
31+
See `.env.example`.
32+
33+
- `PORT` — HTTP port, default `3001`
34+
- `HOST` — bind host, default `127.0.0.1` locally; Docker sets `0.0.0.0` inside the container and the example port mapping still binds to localhost on the host
35+
- `L2S_DATA_DIR` — persistent agent identity/network data directory
36+
- `AGENT_NAME` — display name announced by the dashboard agent
37+
38+
## Health checks
39+
40+
- `GET /healthz` — process is alive
41+
- `GET /api/status` — dashboard agent/runtime status

docs/terminology.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Terminology
2+
3+
`state` is the product term; `group` is the runtime/internal protocol term; they refer to the same encrypted shared context.
4+
5+
Public UI, docs and new CLI aliases should prefer:
6+
7+
- `state` — a shared encrypted context
8+
- `public state` — a discoverable state with a `self.md` manifesto
9+
- `rooms` — conversational/work subspaces inside a state
10+
- `agents` — actors in the network
11+
12+
Existing `group` CLI commands remain for backwards compatibility. Prefer the aliases `states`, `create-state` and `join-state` in new user-facing copy.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
22
"name": "networkselfmd",
33
"private": true,
4+
"packageManager": "pnpm@10.33.2",
45
"type": "module",
56
"scripts": {
67
"build": "pnpm -r build",

packages/cli/src/bin.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@ program
3131
await createGroup(opts.name);
3232
});
3333

34+
program
35+
.command('create-state')
36+
.description('Create a new state (alias for create-group)')
37+
.requiredOption('--name <name>', 'State name')
38+
.action(async (opts) => {
39+
await createGroup(opts.name);
40+
});
41+
3442
program
3543
.command('join-group')
3644
.description('Join an existing group')
@@ -39,6 +47,14 @@ program
3947
await joinGroup(groupId);
4048
});
4149

50+
program
51+
.command('join-state')
52+
.description('Join an existing state (alias for join-group)')
53+
.argument('<stateId>', 'State ID to join')
54+
.action(async (stateId: string) => {
55+
await joinGroup(stateId);
56+
});
57+
4258
program
4359
.command('chat')
4460
.description('Enter interactive chat')
@@ -54,6 +70,13 @@ program
5470
await listGroups();
5571
});
5672

73+
program
74+
.command('states')
75+
.description('List states (alias for groups)')
76+
.action(async () => {
77+
await listGroups();
78+
});
79+
5780
program
5881
.command('peers')
5982
.description('List peers')

packages/dashboard/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"dev": "concurrently \"vite\" \"tsx watch src/server/index.ts\"",
88
"build": "vite build && tsc -p tsconfig.json",
99
"preview": "tsx src/server/index.ts",
10+
"start": "node dist/server/index.js",
1011
"test": "vitest run"
1112
},
1213
"dependencies": {

0 commit comments

Comments
 (0)