Initial public commit
Daybook: a self-hosted, multi-user Django + DRF app where agents, scripts, or people publish artifacts and each logged-in user browses their own time-ordered, label-filterable feed. This squashes prior local development history (kept privately on a separate branch) into a single clean starting point for the public repository. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
commit
940a703cd7
62 changed files with 5755 additions and 0 deletions
24
deploy/Dockerfile
Normal file
24
deploy/Dockerfile
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
FROM python:3.12-slim
|
||||
|
||||
ENV PYTHONDONTWRITEBYTECODE=1 \
|
||||
PYTHONUNBUFFERED=1 \
|
||||
PIP_NO_CACHE_DIR=1
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends curl \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY requirements.txt /app/requirements.txt
|
||||
RUN pip install --upgrade pip \
|
||||
&& pip install -r /app/requirements.txt
|
||||
|
||||
COPY . /app
|
||||
RUN sed -i 's/\r$//' /app/deploy/entrypoint.sh \
|
||||
&& chmod +x /app/deploy/entrypoint.sh
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
ENTRYPOINT ["/app/deploy/entrypoint.sh"]
|
||||
CMD ["gunicorn", "daybook_project.wsgi:application", "--bind", "0.0.0.0:8000", "--workers", "2", "--timeout", "120"]
|
||||
19
deploy/docker-compose.yml
Normal file
19
deploy/docker-compose.yml
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
services:
|
||||
daybook:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: deploy/Dockerfile
|
||||
container_name: daybook
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- .env
|
||||
ports:
|
||||
- "8000:8000"
|
||||
volumes:
|
||||
- ./data:/data
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-fsS", "http://127.0.0.1:8000/api/capabilities"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 20s
|
||||
9
deploy/entrypoint.sh
Normal file
9
deploy/entrypoint.sh
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#!/usr/bin/env sh
|
||||
set -eu
|
||||
|
||||
mkdir -p "${DAYBOOK_DATA_DIR:-/data}" "${ARTIFACTS_ROOT:-/data/artifacts_store}"
|
||||
|
||||
python manage.py migrate --noinput
|
||||
python manage.py collectstatic --noinput
|
||||
|
||||
exec "$@"
|
||||
7
deploy/env.example
Normal file
7
deploy/env.example
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
DJANGO_DEBUG=0
|
||||
DJANGO_SECRET_KEY=replace-with-a-long-random-secret
|
||||
DJANGO_ALLOWED_HOSTS=localhost,127.0.0.1,your-proxmox-container-ip,your-domain.example
|
||||
DAYBOOK_API_TOKEN=replace-with-a-long-random-token
|
||||
DAYBOOK_DATA_DIR=/data
|
||||
DATABASE_PATH=/data/db.sqlite3
|
||||
ARTIFACTS_ROOT=/data/artifacts_store
|
||||
348
deploy/proxmox_llm_deploy.md
Normal file
348
deploy/proxmox_llm_deploy.md
Normal file
|
|
@ -0,0 +1,348 @@
|
|||
# Proxmox Container Deployment Runbook For LLM Operators
|
||||
|
||||
This document is written for an LLM or automation agent that is deploying Daybook into a Linux container on a Proxmox host.
|
||||
|
||||
The recommended deployment target is a Proxmox LXC or VM running Debian/Ubuntu with Docker and Docker Compose. Daybook itself runs as a Docker container inside that guest.
|
||||
|
||||
## Deployment Model
|
||||
|
||||
```text
|
||||
Proxmox host
|
||||
Debian/Ubuntu LXC or VM
|
||||
Docker Engine
|
||||
daybook container
|
||||
/app application code baked into image
|
||||
/data persistent mounted volume
|
||||
```
|
||||
|
||||
Persistent runtime data lives outside the image in `deploy/data` on the guest:
|
||||
|
||||
```text
|
||||
deploy/data/
|
||||
db.sqlite3
|
||||
artifacts_store/
|
||||
```
|
||||
|
||||
Do not store runtime data inside the image.
|
||||
|
||||
## Files In This Directory
|
||||
|
||||
| File | Purpose |
|
||||
|---|---|
|
||||
| `Dockerfile` | Builds the Daybook image. |
|
||||
| `docker-compose.yml` | Runs Daybook with a persistent `./data:/data` bind mount. |
|
||||
| `entrypoint.sh` | Runs migrations, collects static files, then starts Gunicorn. |
|
||||
| `env.example` | Template for deployment environment variables. |
|
||||
| `proxmox_llm_deploy.md` | This runbook. |
|
||||
|
||||
## Prerequisites On The Proxmox Guest
|
||||
|
||||
The guest OS should have:
|
||||
|
||||
- Docker Engine
|
||||
- Docker Compose plugin
|
||||
- Git
|
||||
- Network access to pull Python base images and Python packages
|
||||
|
||||
For Debian/Ubuntu guests, install basics:
|
||||
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y ca-certificates curl git
|
||||
```
|
||||
|
||||
Install Docker using Docker's current official instructions for the guest OS. Then verify:
|
||||
|
||||
```bash
|
||||
docker --version
|
||||
docker compose version
|
||||
```
|
||||
|
||||
## Clone Or Copy The Project
|
||||
|
||||
Choose a stable application path on the guest:
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /opt/daybook
|
||||
sudo chown "$USER:$USER" /opt/daybook
|
||||
```
|
||||
|
||||
If using Git:
|
||||
|
||||
```bash
|
||||
git clone <REPO_URL> /opt/daybook
|
||||
cd /opt/daybook
|
||||
```
|
||||
|
||||
If files were copied by another mechanism, ensure the project root contains:
|
||||
|
||||
```text
|
||||
manage.py
|
||||
requirements.txt
|
||||
deploy/docker-compose.yml
|
||||
deploy/Dockerfile
|
||||
```
|
||||
|
||||
## Create The Environment File
|
||||
|
||||
From the project root:
|
||||
|
||||
```bash
|
||||
cd /opt/daybook
|
||||
cp deploy/env.example deploy/.env
|
||||
```
|
||||
|
||||
Generate secrets:
|
||||
|
||||
```bash
|
||||
python3 - <<'PY'
|
||||
import secrets
|
||||
print('DJANGO_SECRET_KEY=' + secrets.token_urlsafe(50))
|
||||
print('DAYBOOK_API_TOKEN=' + secrets.token_urlsafe(32))
|
||||
PY
|
||||
```
|
||||
|
||||
Edit `deploy/.env`:
|
||||
|
||||
```bash
|
||||
nano deploy/.env
|
||||
```
|
||||
|
||||
Set at minimum:
|
||||
|
||||
```text
|
||||
DJANGO_DEBUG=0
|
||||
DJANGO_SECRET_KEY=<generated-secret>
|
||||
DJANGO_ALLOWED_HOSTS=<container-ip>,<dns-name-if-any>,localhost,127.0.0.1
|
||||
DAYBOOK_API_TOKEN=<generated-token>
|
||||
DAYBOOK_DATA_DIR=/data
|
||||
DATABASE_PATH=/data/db.sqlite3
|
||||
ARTIFACTS_ROOT=/data/artifacts_store
|
||||
```
|
||||
|
||||
Important:
|
||||
|
||||
- Do not commit `deploy/.env`.
|
||||
- Do not use the development token `dev-token` for a real deployment.
|
||||
- Include the actual IP or DNS name users will use in `DJANGO_ALLOWED_HOSTS`.
|
||||
|
||||
## Create Persistent Data Directory
|
||||
|
||||
From the project root:
|
||||
|
||||
```bash
|
||||
mkdir -p deploy/data
|
||||
```
|
||||
|
||||
The compose file mounts this path into the container as `/data`.
|
||||
|
||||
## Build And Start
|
||||
|
||||
From the project root:
|
||||
|
||||
```bash
|
||||
docker compose -f deploy/docker-compose.yml up -d --build
|
||||
```
|
||||
|
||||
Watch logs:
|
||||
|
||||
```bash
|
||||
docker compose -f deploy/docker-compose.yml logs -f daybook
|
||||
```
|
||||
|
||||
Expected startup behavior:
|
||||
|
||||
- Django migrations run.
|
||||
- Static files are collected.
|
||||
- Gunicorn starts on port `8000` inside the container.
|
||||
- Host port `8000` maps to container port `8000`.
|
||||
|
||||
## Verify Deployment
|
||||
|
||||
Check container state:
|
||||
|
||||
```bash
|
||||
docker compose -f deploy/docker-compose.yml ps
|
||||
```
|
||||
|
||||
Check capabilities endpoint from the guest:
|
||||
|
||||
```bash
|
||||
curl -fsS http://127.0.0.1:8000/api/capabilities
|
||||
```
|
||||
|
||||
Expected JSON includes:
|
||||
|
||||
```json
|
||||
{
|
||||
"artifact_kinds": ["html_fragment", "html_full", "markdown", "deck", "image", "pdf", "binary"],
|
||||
"auth": "bearer_token"
|
||||
}
|
||||
```
|
||||
|
||||
Open the app from another machine:
|
||||
|
||||
```text
|
||||
http://<guest-ip>:8000/
|
||||
```
|
||||
|
||||
If this fails:
|
||||
|
||||
- Confirm the Proxmox guest firewall allows TCP `8000`.
|
||||
- Confirm any Proxmox firewall rules allow TCP `8000`.
|
||||
- Confirm `DJANGO_ALLOWED_HOSTS` includes the hostname/IP being used.
|
||||
|
||||
## Publish A Smoke-Test Artifact
|
||||
|
||||
Replace `<TOKEN>` with `DAYBOOK_API_TOKEN` from `deploy/.env`.
|
||||
|
||||
```bash
|
||||
curl -fsS -X POST http://127.0.0.1:8000/api/artifacts \
|
||||
-H "Authorization: Bearer <TOKEN>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"persona": "deploy-smoke",
|
||||
"source_type": "agent",
|
||||
"source_name": "Deployment Smoke Agent",
|
||||
"kind": "markdown",
|
||||
"title": "Deployment Smoke Test",
|
||||
"content": "# Deployment Smoke Test\n\nDaybook accepted an artifact.",
|
||||
"tags": ["deploy", "smoke"],
|
||||
"metadata": {"run_id": "deploy-smoke-001"}
|
||||
}'
|
||||
```
|
||||
|
||||
Then refresh:
|
||||
|
||||
```text
|
||||
http://<guest-ip>:8000/
|
||||
```
|
||||
|
||||
## Operational Commands
|
||||
|
||||
Stop:
|
||||
|
||||
```bash
|
||||
docker compose -f deploy/docker-compose.yml down
|
||||
```
|
||||
|
||||
Start:
|
||||
|
||||
```bash
|
||||
docker compose -f deploy/docker-compose.yml up -d
|
||||
```
|
||||
|
||||
Rebuild after code changes:
|
||||
|
||||
```bash
|
||||
docker compose -f deploy/docker-compose.yml up -d --build
|
||||
```
|
||||
|
||||
View logs:
|
||||
|
||||
```bash
|
||||
docker compose -f deploy/docker-compose.yml logs -f daybook
|
||||
```
|
||||
|
||||
Run Django management command:
|
||||
|
||||
```bash
|
||||
docker compose -f deploy/docker-compose.yml exec daybook python manage.py check
|
||||
```
|
||||
|
||||
Open shell:
|
||||
|
||||
```bash
|
||||
docker compose -f deploy/docker-compose.yml exec daybook sh
|
||||
```
|
||||
|
||||
## Backup
|
||||
|
||||
Back up the guest directory:
|
||||
|
||||
```text
|
||||
/opt/daybook/deploy/data
|
||||
```
|
||||
|
||||
It contains:
|
||||
|
||||
- SQLite database
|
||||
- Artifact files
|
||||
|
||||
Simple backup command:
|
||||
|
||||
```bash
|
||||
tar -czf daybook-data-$(date +%Y%m%d-%H%M%S).tar.gz -C /opt/daybook/deploy data
|
||||
```
|
||||
|
||||
For consistent backups during heavy writes, stop the container first:
|
||||
|
||||
```bash
|
||||
docker compose -f /opt/daybook/deploy/docker-compose.yml down
|
||||
tar -czf daybook-data-$(date +%Y%m%d-%H%M%S).tar.gz -C /opt/daybook/deploy data
|
||||
docker compose -f /opt/daybook/deploy/docker-compose.yml up -d
|
||||
```
|
||||
|
||||
## Restore
|
||||
|
||||
Stop Daybook:
|
||||
|
||||
```bash
|
||||
docker compose -f /opt/daybook/deploy/docker-compose.yml down
|
||||
```
|
||||
|
||||
Replace `/opt/daybook/deploy/data` with the restored data directory, then start:
|
||||
|
||||
```bash
|
||||
docker compose -f /opt/daybook/deploy/docker-compose.yml up -d
|
||||
```
|
||||
|
||||
## Updating The App
|
||||
|
||||
If deployed from Git:
|
||||
|
||||
```bash
|
||||
cd /opt/daybook
|
||||
git pull
|
||||
docker compose -f deploy/docker-compose.yml up -d --build
|
||||
```
|
||||
|
||||
The container entrypoint runs migrations on startup.
|
||||
|
||||
## Reverse Proxy Notes
|
||||
|
||||
If putting Daybook behind Nginx, Caddy, Traefik, or a Proxmox-level reverse proxy:
|
||||
|
||||
- Proxy to `http://<guest-ip>:8000`.
|
||||
- Add the public hostname to `DJANGO_ALLOWED_HOSTS`.
|
||||
- Terminate TLS at the proxy.
|
||||
- Keep `DJANGO_DEBUG=0`.
|
||||
|
||||
Example `DJANGO_ALLOWED_HOSTS`:
|
||||
|
||||
```text
|
||||
DJANGO_ALLOWED_HOSTS=daybook.example.com,localhost,127.0.0.1
|
||||
```
|
||||
|
||||
## LLM Deployment Checklist
|
||||
|
||||
Before declaring deployment complete, verify all items:
|
||||
|
||||
- `deploy/.env` exists and does not use placeholder secrets.
|
||||
- `DJANGO_DEBUG=0`.
|
||||
- `DJANGO_ALLOWED_HOSTS` includes the access hostname/IP.
|
||||
- `deploy/data/` exists.
|
||||
- `docker compose -f deploy/docker-compose.yml ps` shows `daybook` running or healthy.
|
||||
- `curl -fsS http://127.0.0.1:8000/api/capabilities` succeeds on the guest.
|
||||
- Browser opens `http://<guest-ip>:8000/`.
|
||||
- Smoke-test artifact POST succeeds with the configured bearer token.
|
||||
- `deploy/data/db.sqlite3` exists after startup.
|
||||
- `deploy/data/artifacts_store/` exists after posting an artifact.
|
||||
|
||||
## Known MVP Constraints
|
||||
|
||||
- SQLite is used by default. This is acceptable for local/self-hosted MVP usage.
|
||||
- The API uses a single shared bearer token.
|
||||
- No reader authentication is implemented yet.
|
||||
- Manual drag/drop upload is deferred.
|
||||
- Content revisions are deferred.
|
||||
Loading…
Add table
Add a link
Reference in a new issue