5 Commits
Author SHA1 Message Date
ksmithandClaude Sonnet 5 b1c9a85ab3 Bump to v2026.9.4
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-04 21:38:02 -06:00
ksmithandClaude Sonnet 5 62a25c6278 Point public deploy docs at the real GitHub repo
DEPLOYMENT.md's clone instructions and frontend/.env.example's
VITE_SOURCE_URL default now name github.com/ds-ksmith/DS-Chat directly
now that the project actually has a public home there, instead of
generic placeholders -- with a note for anyone deploying from their
own fork. The user's own production deploy is unaffected: it still
pulls from their private Gitea instance, which mirrors to GitHub.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-04 21:37:35 -06:00
ksmithandClaude Sonnet 5 7a84d2f09f Deploy and upgrade only from tagged releases, not branch tip
deploy/upgrade.sh now fetches tags and checks out whichever sorts
newest (detached HEAD) instead of git pull --ff-only on a branch, now
that the project has a real release process (dated tags, e.g.
v2026.9.3) and a public mirror. Keeps production from ever landing on
an untagged commit. DEPLOYMENT.md's initial-clone steps and Upgrades
section updated to match.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-04 21:31:08 -06:00
ksmithandClaude Sonnet 5 d74527a29c Stop tracking .claude/ -- local tool config, not project source
launch.json is just the Browser preview tool's dev-server launch
config (command, port); nothing sensitive in it, but it never should
have been committed in the first place.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-04 21:31:00 -06:00
ksmithandClaude Sonnet 5 f0e4c76ffd Prepare for public release: de-identify docs, add contributor terms
Removes personal infra baked into copy-paste instructions and the
app's own UI ahead of an eventual public release:

- AboutModal's "Source code" link is now a build-time env var
  (VITE_SOURCE_URL) instead of a hardcoded personal Gitea URL, and
  hides itself when unset rather than pointing somewhere wrong
- DEPLOYMENT.md's clone steps are genericized to any git host
- LICENSE gets its previously-blank copyright/description lines filled in
- CONTRIBUTING.md adds a lightweight contributor-terms note to keep a
  future dual-licensed offering possible once outside PRs start arriving

Deliberately out of scope for now: git commit history (still under the
real author identity) and the actual publish destination -- both still
undecided.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-04 21:01:33 -06:00
11 changed files with 123 additions and 46 deletions
-17
View File
@@ -1,17 +0,0 @@
{
"version": "0.0.1",
"configurations": [
{
"name": "frontend",
"runtimeExecutable": "npm",
"runtimeArgs": ["--prefix", "frontend", "run", "dev"],
"port": 5173
},
{
"name": "frontend-preview",
"runtimeExecutable": "npm",
"runtimeArgs": ["--prefix", "frontend", "run", "preview"],
"port": 4173
}
]
}
+1
View File
@@ -17,6 +17,7 @@ dist-ssr/
## Editors / OS ## Editors / OS
.vscode/ .vscode/
.idea/ .idea/
.claude/
.DS_Store .DS_Store
## Test / coverage ## Test / coverage
+51
View File
@@ -0,0 +1,51 @@
# Contributing to DS Chat
Thanks for considering a contribution.
## Reporting bugs and requesting features
Open an issue on this project's issue tracker. Include steps to reproduce
for a bug, or the problem you're trying to solve for a feature request —
that's usually more useful than a proposed solution.
## Development setup
See the root [README.md](README.md)'s Quickstart, plus
[backend/README.md](backend/README.md) and
[frontend/README.md](frontend/README.md) for the full local dev setup
(Postgres, Redis, Python venv, migrations, the Vite dev server).
[ARCHITECTURE.md](ARCHITECTURE.md) covers the overall system design if
you're orienting yourself before a larger change.
## Before opening a pull request
- **Tests**: run `pytest` in `backend/` for any backend change, and add
tests for new behavior rather than just the happy path — see
`backend/README.md`'s "Run tests" section. For frontend changes, run
`npx tsc -b` in `frontend/` and confirm `npm run build` succeeds.
- **Style**: match the conventions already in the file you're editing
rather than introducing a new pattern — this codebase doesn't have a
separate style guide beyond "look at what's already there."
- **Scope**: smaller, focused PRs are easier to review than large ones
that mix unrelated changes.
## Contributor terms
By submitting a contribution (a pull request, patch, or similar), you
agree that:
1. Your contribution is licensed under the project's own license,
AGPL-3.0-or-later ([LICENSE](LICENSE)), and
2. You grant the project's maintainer(s) a perpetual, worldwide,
non-exclusive right to also relicense your contribution under different
terms — for example, as part of a separately-licensed commercial
offering built on this project.
This keeps the option of a future dual-licensed (open-source +
commercial) version of the project available, without requiring a
separate signed agreement for every contribution.
*This is a lightweight starting point, not a substitute for legal advice —
if you're contributing something substantial, or maintaining a fork with
your own commercial plans, it's worth having this reviewed by a lawyer
rather than relying on the paragraph above alone.*
+37 -15
View File
@@ -132,35 +132,45 @@ sudo -u ds-chat ssh-keygen -t ed25519 -f /srv/ds-chat/.ssh/id_ed25519 -N ""
sudo cat /srv/ds-chat/.ssh/id_ed25519.pub sudo cat /srv/ds-chat/.ssh/id_ed25519.pub
``` ```
Add that public key as a **read-only deploy key** on the Gitea repo This project is hosted at
(Settings → Deploy Keys), then: **[github.com/ds-ksmith/DS-Chat](https://github.com/ds-ksmith/DS-Chat)**.
Add that public key there as a **read-only deploy key** (Settings → Deploy
Keys on the repo), then:
```bash ```bash
sudo -u ds-chat ssh-keyscan git.darksingularity.org >> /srv/ds-chat/.ssh/known_hosts sudo -u ds-chat ssh-keyscan github.com >> /srv/ds-chat/.ssh/known_hosts
sudo -u ds-chat git clone git@git.darksingularity.org:DarkSingularity/ds-chat.git /srv/ds-chat sudo -u ds-chat git clone git@github.com:ds-ksmith/DS-Chat.git /srv/ds-chat
``` ```
(If your Gitea's SSH is on a non-default port, adjust the clone URL and (Deploying from your own fork instead? Substitute its clone URL — the same
`ssh-keyscan -p <port>` accordingly.) deploy-key/access-token steps work the same way on GitHub, GitLab, Gitea,
and most other git hosts.)
**Alternative: a personal/deployment-user access token instead of a deploy **Alternative: a personal/deployment-user access token instead of a deploy
key** — skip the `.ssh`/`ssh-keygen`/`ssh-keyscan` commands above entirely key** — skip the `.ssh`/`ssh-keygen`/`ssh-keyscan` commands above entirely
and clone over HTTPS with the token embedded in the URL: and clone over HTTPS with the token embedded in the URL:
```bash ```bash
sudo -u ds-chat git clone https://<TOKEN>@git.darksingularity.org/DarkSingularity/ds-chat.git /srv/ds-chat sudo -u ds-chat git clone https://<TOKEN>@github.com/ds-ksmith/DS-Chat.git /srv/ds-chat
``` ```
The token then lives in plaintext in `/srv/ds-chat/.git/config` (`git The token then lives in plaintext in `/srv/ds-chat/.git/config` (`git
remote -v` shows it) — readable by root and the `ds-chat` user, not by remote -v` shows it) — readable by root and the `ds-chat` user, not by
anyone else under normal file permissions. `deploy/upgrade.sh`'s later anyone else under normal file permissions. `deploy/upgrade.sh`'s later
`git pull`s reuse this same authenticated URL automatically, no extra `git fetch`es reuse this same authenticated URL automatically, no extra
setup needed. Fine as long as the token is scoped to read-only access on setup needed. Fine as long as the token is scoped to read-only access on
just this repo. just this repo.
Either way, now that the repo is cloned: Either way, now that the repo is cloned, check out the latest release tag
rather than deploying whatever the default branch's tip happens to be —
`deploy/upgrade.sh` follows the same rule on every later upgrade (see §6),
so this keeps the very first deploy consistent with all the ones after it:
```bash ```bash
cd /srv/ds-chat
sudo -u ds-chat git fetch --tags
LATEST_TAG="$(sudo -u ds-chat git tag --sort=-creatordate | head -n1)"
sudo -u ds-chat git checkout --detach "$LATEST_TAG"
sudo -u ds-chat mkdir -p /srv/ds-chat/uploads sudo -u ds-chat mkdir -p /srv/ds-chat/uploads
``` ```
@@ -232,7 +242,16 @@ admin sets it up.
`/ws`) whenever that directory exists — that's what lets Nginx Proxy `/ws`) whenever that directory exists — that's what lets Nginx Proxy
Manager forward the whole domain to one port with no custom path routing. Manager forward the whole domain to one port with no custom path routing.
Before building, copy `frontend/.env.example` to `frontend/.env.production`
and set `VITE_SOURCE_URL` to wherever *your* copy of the repo lives — see
that file's own comment for why this matters (AGPL-3.0 source-availability
compliance). Vite bakes this in at build time, so it needs to be in place
before `npm run build` runs, and needs re-running after any future change
to it.
```bash ```bash
sudo -u ds-chat cp /srv/ds-chat/frontend/.env.example /srv/ds-chat/frontend/.env.production
sudo -u ds-chat nano /srv/ds-chat/frontend/.env.production # set VITE_SOURCE_URL
sudo -u ds-chat bash -c 'cd /srv/ds-chat/frontend && npm ci && npm run build' sudo -u ds-chat bash -c 'cd /srv/ds-chat/frontend && npm ci && npm run build'
``` ```
@@ -320,12 +339,15 @@ This is config in NPM's own UI/database, not a file this repo ships:
sudo -u ds-chat /srv/ds-chat/deploy/upgrade.sh sudo -u ds-chat /srv/ds-chat/deploy/upgrade.sh
``` ```
Pulls latest `main`, reinstalls backend deps, runs `alembic upgrade head`, Fetches tags and checks out whichever one sorts newest (`git tag
rebuilds the frontend, restarts `ds-chat`, and curls `/api/health` to --sort=-creatordate`) — deliberately not the default branch's tip, so
confirm it came back up. Fails loudly (`set -euo pipefail`) and stops running this between releases is a safe no-op rather than pulling in
before restarting anything if an earlier step — most importantly a failed whatever's mid-flight on `main`. Then reinstalls backend deps, runs
migration — errors out, so a bad deploy doesn't take down the previously `alembic upgrade head`, rebuilds the frontend, restarts `ds-chat`, and
working one. curls `/api/health` to confirm it came back up. Fails loudly
(`set -euo pipefail`) and stops before restarting anything if an earlier
step — most importantly a failed migration — errors out, so a bad deploy
doesn't take down the previously working one.
Active users get disconnected for a few seconds during the restart and Active users get disconnected for a few seconds during the restart and
reconnect automatically (same reconnect logic as §4's NPM-timeout note) — reconnect automatically (same reconnect logic as §4's NPM-timeout note) —
+2 -2
View File
@@ -629,8 +629,8 @@ to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found. the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.> DS Chat, a self-hosted, real-time team chat service.
Copyright (C) <year> <name of author> Copyright (C) 2026 Keith Smith
This program is free software: you can redistribute it and/or modify This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by it under the terms of the GNU Affero General Public License as published by
+1 -1
View File
@@ -1,6 +1,6 @@
[project] [project]
name = "ds-chat" name = "ds-chat"
version = "2026.9.3" version = "2026.9.4"
description = "DS Chat backend service" description = "DS Chat backend service"
license = { text = "AGPL-3.0-or-later" } license = { text = "AGPL-3.0-or-later" }
requires-python = ">=3.11" requires-python = ">=3.11"
+11 -2
View File
@@ -16,9 +16,18 @@ BACKEND_DIR="${REPO_DIR}/backend"
FRONTEND_DIR="${REPO_DIR}/frontend" FRONTEND_DIR="${REPO_DIR}/frontend"
ENV_FILE="/etc/ds-chat/env" ENV_FILE="/etc/ds-chat/env"
echo "==> Pulling latest code" echo "==> Fetching latest release"
cd "$REPO_DIR" cd "$REPO_DIR"
git pull --ff-only git fetch --tags --force
LATEST_TAG="$(git tag --sort=-creatordate | head -n1)"
if [[ -z "$LATEST_TAG" ]]; then
echo "No tags found -- nothing to deploy" >&2
exit 1
fi
echo "Deploying $LATEST_TAG"
# Detached HEAD, not a branch checkout -- this directory only ever runs a
# tagged release, never whatever the default branch's tip happens to be.
git checkout --quiet --detach "$LATEST_TAG"
echo "==> Installing backend dependencies" echo "==> Installing backend dependencies"
cd "$BACKEND_DIR" cd "$BACKEND_DIR"
+7
View File
@@ -0,0 +1,7 @@
# Shown as the "Source code" link in the app's About screen -- required
# for AGPL-3.0 section 13 compliance once you deploy this (a link so users
# interacting with the app over the network can get the actual source,
# including any modifications you've made). The default below points at
# the upstream project -- fine if you're running it unmodified, but if
# you've forked or patched the code, point this at *your* copy instead.
VITE_SOURCE_URL=https://github.com/ds-ksmith/DS-Chat
+1 -1
View File
@@ -112,7 +112,7 @@ src/
LoginPage.tsx, SignupPage.tsx, ForgotPasswordPage.tsx, ResetPasswordPage.tsx LoginPage.tsx, SignupPage.tsx, ForgotPasswordPage.tsx, ResetPasswordPage.tsx
ChatShellPage.tsx, AdminPage.tsx, HelpPage.tsx ChatShellPage.tsx, AdminPage.tsx, HelpPage.tsx
styles/tokens.css design tokens (DarkSingularity theme: colors, spacing, etc.) styles/tokens.css design tokens (default theme: colors, spacing, etc.)
sw.ts custom service worker (injectManifest): app-shell sw.ts custom service worker (injectManifest): app-shell
precache + NetworkFirst runtime caching, push/notificationclick precache + NetworkFirst runtime caching, push/notificationclick
handlers, SKIP_WAITING messaging for the update-prompt flow handlers, SKIP_WAITING messaging for the update-prompt flow
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "frontend", "name": "frontend",
"private": true, "private": true,
"version": "2026.9.3", "version": "2026.9.4",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"type": "module", "type": "module",
"scripts": { "scripts": {
+11 -7
View File
@@ -10,8 +10,10 @@ interface AboutModalProps {
// users remotely through a computer network, you should also make sure // users remotely through a computer network, you should also make sure
// that it provides a way for users to get its source... its interface // that it provides a way for users to get its source... its interface
// could display a 'Source' link" -- this modal is that link, not just a // could display a 'Source' link" -- this modal is that link, not just a
// courtesy credits screen. // courtesy credits screen. Deliberately not a hardcoded URL: whoever
const SOURCE_URL = 'https://git.darksingularity.org/DarkSingularity/ds-chat' // deploys this needs to point it at *their* copy of the repo (including
// any modifications), not the upstream project -- see frontend/.env.example.
const SOURCE_URL = import.meta.env.VITE_SOURCE_URL as string | undefined
export function AboutModal({ onClose }: AboutModalProps) { export function AboutModal({ onClose }: AboutModalProps) {
return ( return (
@@ -39,11 +41,13 @@ export function AboutModal({ onClose }: AboutModalProps) {
</a> </a>
. .
</p> </p>
<p className="about-modal-line"> {SOURCE_URL && (
<a href={SOURCE_URL} target="_blank" rel="noopener noreferrer"> <p className="about-modal-line">
Source code <a href={SOURCE_URL} target="_blank" rel="noopener noreferrer">
</a> Source code
</p> </a>
</p>
)}
<div className="modal-actions" style={{ marginTop: '1rem' }}> <div className="modal-actions" style={{ marginTop: '1rem' }}>
<button type="button" className="btn-secondary" onClick={onClose}> <button type="button" className="btn-secondary" onClick={onClose}>