Skip to content

Commit 3742f71

Browse files
nullarchclaude
andcommitted
chore: launch-readiness — CI, community health, and AI-path fixes
CI (type-check + build on a pgvector service), SECURITY.md, CODE_OF_CONDUCT.md, issue/PR templates, plus fixes from a pre-launch audit of the AI golden path (EMBEDDING_DIM wiring, answer-model thinking budget, attachment handoff, router bias, SSRF guard on URL ingestion, embedding order) and VERIFYING.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 69a6eff commit 3742f71

14 files changed

Lines changed: 497 additions & 25 deletions

File tree

.env.example

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@ APP_URL=http://localhost:3000 # loader/iframe origin (CSP + email links
88
ANTHROPIC_API_KEY=sk-ant-...
99
EMBEDDINGS_PROVIDER=voyage # voyage | openai | local(bge-m3)
1010
EMBEDDINGS_API_KEY= # provider key (omit for self-hosted bge-m3)
11-
EMBEDDINGS_MODEL=voyage-3 # must match EMBEDDING_DIM
12-
EMBEDDING_DIM=1024 # 1024 voyage/bge-m3 ; 1536 openai text-embedding-3-small
13-
# (must match knowledge_chunk.embedding vector(N))
11+
EMBEDDINGS_MODEL=voyage-3 # must produce EMBEDDING_DIM-length vectors
12+
EMBEDDING_DIM=1024 # 1024 voyage/bge-m3 · 1536 openai text-embedding-3-small
13+
# db:migrate sizes knowledge_chunk.embedding to this
14+
# value — re-run db:reset if you change it later
1415
# ANTHROPIC_MODEL_ROUTER=claude-haiku-4-5-20251001
1516
# ANTHROPIC_MODEL_ANSWER=claude-sonnet-5
1617
# ANTHROPIC_MODEL_HARD=claude-opus-4-8
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
name: Bug report
3+
about: Something isn't working as expected
4+
title: ""
5+
labels: bug
6+
assignees: ""
7+
---
8+
9+
**What happened?**
10+
A clear description of the bug.
11+
12+
**Steps to reproduce**
13+
1.
14+
2.
15+
3.
16+
17+
**Expected behavior**
18+
What you expected to happen instead.
19+
20+
**Environment**
21+
- Deployment: `docker compose` / local dev / managed host
22+
- openConcierge commit or version:
23+
- Postgres + pgvector version:
24+
- Embeddings provider: voyage / openai / local (bge-m3)
25+
- Browser (if this is a widget or desk issue):
26+
27+
**Logs / screenshots**
28+
Paste relevant server logs or screenshots. **Scrub any secrets and customer PII first.**

.github/ISSUE_TEMPLATE/config.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
blank_issues_enabled: true
2+
contact_links:
3+
- name: Report a security vulnerability
4+
url: https://github.com/nullarch/openconcierge/security/advisories/new
5+
about: Please report security issues privately here, never as a public issue.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
name: Feature request
3+
about: Suggest an idea or improvement
4+
title: ""
5+
labels: enhancement
6+
assignees: ""
7+
---
8+
9+
**Problem**
10+
What are you trying to do that's hard or impossible today?
11+
12+
**Proposed solution**
13+
What you'd like to see.
14+
15+
**Alternatives considered**
16+
Other approaches you've thought about.
17+
18+
**Scope check**
19+
Does this fit the MVP wedge (an AI concierge + self-hosted support desk), or is
20+
it a post-MVP item from the roadmap? Link the relevant roadmap point if you can.

.github/PULL_REQUEST_TEMPLATE.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!-- Thanks for contributing to openConcierge! -->
2+
3+
## What & why
4+
<!-- What does this change, and why? Link any related issue, e.g. "Closes #123". -->
5+
6+
## How to test
7+
<!-- Steps for a reviewer to verify the change. -->
8+
9+
## Checklist
10+
- [ ] `npm run build` passes (type-check + build)
11+
- [ ] DB changes are a **new** timestamped file in `db/migrations/` (not an edit to an already-applied one)
12+
- [ ] No secrets, API keys, or customer PII in the diff
13+
- [ ] README / docs updated if behavior or setup changed

.github/workflows/ci.yml

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
8+
# Cancel superseded runs on the same ref.
9+
concurrency:
10+
group: ci-${{ github.ref }}
11+
cancel-in-progress: true
12+
13+
jobs:
14+
build:
15+
name: Type-check and build
16+
runs-on: ubuntu-latest
17+
18+
services:
19+
postgres:
20+
image: pgvector/pgvector:pg16
21+
env:
22+
POSTGRES_USER: openconcierge
23+
POSTGRES_PASSWORD: openconcierge
24+
POSTGRES_DB: openconcierge
25+
ports:
26+
- 5432:5432
27+
options: >-
28+
--health-cmd "pg_isready -U openconcierge"
29+
--health-interval 5s
30+
--health-timeout 5s
31+
--health-retries 10
32+
33+
env:
34+
DATABASE_URL: postgres://openconcierge:openconcierge@localhost:5432/openconcierge
35+
APP_URL: http://localhost:3000
36+
# Dummy AI config so the production build never needs real keys.
37+
# (Keys are read at request time, not at build time.)
38+
ANTHROPIC_API_KEY: build-only-not-used
39+
EMBEDDINGS_PROVIDER: voyage
40+
EMBEDDINGS_API_KEY: build-only-not-used
41+
EMBEDDINGS_MODEL: voyage-3
42+
EMBEDDING_DIM: "1024"
43+
44+
steps:
45+
- uses: actions/checkout@v4
46+
47+
- uses: actions/setup-node@v4
48+
with:
49+
node-version: 20
50+
cache: npm
51+
52+
- name: Install dependencies
53+
run: npm ci
54+
55+
- name: Apply migrations + seed
56+
run: npm run db:migrate
57+
58+
- name: Build (includes the TypeScript type-check)
59+
run: npm run build

CODE_OF_CONDUCT.md

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
# Contributor Covenant Code of Conduct
2+
3+
## Our Pledge
4+
5+
We as members, contributors, and leaders pledge to make participation in our
6+
community a harassment-free experience for everyone, regardless of age, body
7+
size, visible or invisible disability, ethnicity, sex characteristics, gender
8+
identity and expression, level of experience, education, socio-economic status,
9+
nationality, personal appearance, race, caste, color, religion, or sexual
10+
identity and orientation.
11+
12+
We pledge to act and interact in ways that contribute to an open, welcoming,
13+
diverse, inclusive, and healthy community.
14+
15+
## Our Standards
16+
17+
Examples of behavior that contributes to a positive environment for our
18+
community include:
19+
20+
- Demonstrating empathy and kindness toward other people
21+
- Being respectful of differing opinions, viewpoints, and experiences
22+
- Giving and gracefully accepting constructive feedback
23+
- Accepting responsibility and apologizing to those affected by our mistakes,
24+
and learning from the experience
25+
- Focusing on what is best not just for us as individuals, but for the overall
26+
community
27+
28+
Examples of unacceptable behavior include:
29+
30+
- The use of sexualized language or imagery, and sexual attention or advances of
31+
any kind
32+
- Trolling, insulting or derogatory comments, and personal or political attacks
33+
- Public or private harassment
34+
- Publishing others' private information, such as a physical or email address,
35+
without their explicit permission
36+
- Other conduct which could reasonably be considered inappropriate in a
37+
professional setting
38+
39+
## Enforcement Responsibilities
40+
41+
Community leaders are responsible for clarifying and enforcing our standards of
42+
acceptable behavior and will take appropriate and fair corrective action in
43+
response to any behavior that they deem inappropriate, threatening, offensive,
44+
or harmful.
45+
46+
Community leaders have the right and responsibility to remove, edit, or reject
47+
comments, commits, code, wiki edits, issues, and other contributions that are
48+
not aligned to this Code of Conduct, and will communicate reasons for moderation
49+
decisions when appropriate.
50+
51+
## Scope
52+
53+
This Code of Conduct applies within all community spaces, and also applies when
54+
an individual is officially representing the community in public spaces.
55+
Examples of representing our community include using an official email address,
56+
posting via an official social media account, or acting as an appointed
57+
representative at an online or offline event.
58+
59+
## Enforcement
60+
61+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
62+
reported to the community leaders responsible for enforcement privately through
63+
the repository's [Security Advisories](https://github.com/nullarch/openconcierge/security/advisories/new)
64+
or by direct message to a maintainer. All complaints will be reviewed and
65+
investigated promptly and fairly.
66+
67+
All community leaders are obligated to respect the privacy and security of the
68+
reporter of any incident.
69+
70+
## Enforcement Guidelines
71+
72+
Community leaders will follow these Community Impact Guidelines in determining
73+
the consequences for any action they deem in violation of this Code of Conduct:
74+
75+
### 1. Correction
76+
77+
**Community Impact**: Use of inappropriate language or other behavior deemed
78+
unprofessional or unwelcome in the community.
79+
80+
**Consequence**: A private, written warning from community leaders, providing
81+
clarity around the nature of the violation and an explanation of why the
82+
behavior was inappropriate. A public apology may be requested.
83+
84+
### 2. Warning
85+
86+
**Community Impact**: A violation through a single incident or series of
87+
actions.
88+
89+
**Consequence**: A warning with consequences for continued behavior. No
90+
interaction with the people involved, including unsolicited interaction with
91+
those enforcing the Code of Conduct, for a specified period of time. This
92+
includes avoiding interactions in community spaces as well as external channels
93+
like social media. Violating these terms may lead to a temporary or permanent
94+
ban.
95+
96+
### 3. Temporary Ban
97+
98+
**Community Impact**: A serious violation of community standards, including
99+
sustained inappropriate behavior.
100+
101+
**Consequence**: A temporary ban from any sort of interaction or public
102+
communication with the community for a specified period of time. No public or
103+
private interaction with the people involved, including unsolicited interaction
104+
with those enforcing the Code of Conduct, is allowed during this period.
105+
Violating these terms may lead to a permanent ban.
106+
107+
### 4. Permanent Ban
108+
109+
**Community Impact**: Demonstrating a pattern of violation of community
110+
standards, including sustained inappropriate behavior, harassment of an
111+
individual, or aggression toward or disparagement of classes of individuals.
112+
113+
**Consequence**: A permanent ban from any sort of public interaction within the
114+
community.
115+
116+
## Attribution
117+
118+
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
119+
version 2.1, available at
120+
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
121+
122+
Community Impact Guidelines were inspired by
123+
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
124+
125+
For answers to common questions about this code of conduct, see the FAQ at
126+
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
127+
[https://www.contributor-covenant.org/translations][translations].
128+
129+
[homepage]: https://www.contributor-covenant.org
130+
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
131+
[Mozilla CoC]: https://github.com/mozilla/diversity
132+
[FAQ]: https://www.contributor-covenant.org/faq
133+
[translations]: https://www.contributor-covenant.org/translations

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,8 @@ npm run dev # → http://localhost:3000
159159

160160
`npm run build` runs the production build **and** the TypeScript type-check. `npm run db:reset` drops and re-applies migrations + seed.
161161

162+
> **Verifying the AI concierge works?** The live golden path (grounded answer + citations + handoff) needs your keys and a pgvector Postgres — see **[VERIFYING.md](VERIFYING.md)**.
163+
162164
### Managed hosting
163165

164166
Runs on any Node host (Fly.io, Railway, Render, a plain VM) against a managed Postgres that has `pgvector` (Neon, Supabase Postgres, RDS, Crunchy…). Set the env below and run `npm run db:migrate` once.

SECURITY.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Security Policy
2+
3+
openConcierge is self-hosted software that handles customer conversations and
4+
personal data. We take security seriously and appreciate responsible disclosure.
5+
6+
## Supported versions
7+
8+
openConcierge is pre-1.0 and moves quickly. Security fixes land on `main`
9+
please run a recent commit. There are no long-term-support branches yet.
10+
11+
## Reporting a vulnerability
12+
13+
**Please do not open a public issue for security problems.**
14+
15+
Report privately through GitHub Security Advisories:
16+
17+
👉 https://github.com/nullarch/openconcierge/security/advisories/new
18+
19+
Please include a description, reproduction steps, the affected version/commit,
20+
and the impact. We aim to acknowledge within 72 hours and to ship a fix or
21+
mitigation as quickly as the severity warrants. We're happy to credit you unless
22+
you'd prefer to stay anonymous.
23+
24+
## Scope
25+
26+
Because you self-host, your deployment's security is ultimately yours. The
27+
highest-value reports concern the **code itself**:
28+
29+
- authentication / session handling (`src/lib/auth.ts`, `src/lib/password.ts`)
30+
- the widget ↔ desk trust boundary and anonymous-visitor identity
31+
- SQL construction (`src/lib/db.ts` and callers)
32+
- file-upload handling (`src/lib/storage.ts`, the upload routes)
33+
- SSRF via URL ingestion in the knowledge base (`src/lib/ai/`)
34+
- secret handling and accidental exposure
35+
36+
Misconfigured infrastructure (a publicly reachable `DATABASE_URL`, missing TLS,
37+
a committed `.env`) is out of scope, but the README's self-hosting section has
38+
hardening notes to help you avoid it.

VERIFYING.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Verifying the AI concierge
2+
3+
`npm run build` and CI verify types + schema, but the AI path (retrieval →
4+
grounded answer → citations → handoff) only runs with **real keys against a
5+
pgvector Postgres**. This is the ~2-minute check that the live golden path works.
6+
7+
## 1. Run with keys + pgvector
8+
9+
The bundled Docker stack ships pgvector; a bare local Postgres often doesn't
10+
(`create extension vector` will fail if the extension isn't installed).
11+
12+
```bash
13+
cp .env.example .env
14+
# In .env, set at least:
15+
# ANTHROPIC_API_KEY=sk-ant-...
16+
# EMBEDDINGS_API_KEY=... (Voyage or OpenAI)
17+
# EMBEDDINGS_PROVIDER / _MODEL / EMBEDDING_DIM (defaults: voyage-3 / 1024)
18+
docker compose up # → http://localhost:3000
19+
```
20+
21+
Your key stays in `.env` — never paste it into a shell that logs it, or into a PR.
22+
If you change `EMBEDDING_DIM`, run `npm run db:reset` (it re-sizes the vector column).
23+
24+
## 2. Feed it knowledge
25+
26+
Open http://localhost:3000 → sign up (the first user becomes the owner) →
27+
**Knowledge** → add a source (paste an FAQ, or a public URL) → wait for status
28+
**ready**.
29+
30+
Confirm it actually embedded:
31+
32+
```sql
33+
select title, status, chunk_count from knowledge_source;
34+
-- want: status = 'ready', chunk_count > 0
35+
```
36+
37+
If it's stuck at **error**, the `error` column says why (wrong `EMBEDDING_DIM`,
38+
bad embeddings key, or a private URL blocked by the SSRF guard).
39+
40+
## 3. Ask — and watch it answer
41+
42+
Open http://localhost:3000/embed-test.html → open the bubble → ask something the
43+
source covers.
44+
45+
**Expect:** a grounded answer with a **Source** chip within a few seconds.
46+
47+
Then ask something off-topic, or type "I'd like to talk to a person" → **expect a
48+
handoff** ("someone will reply shortly"), and the conversation appears in `/desk`
49+
flagged *needs human*. Reply from the desk and watch it arrive in the widget live.
50+
51+
## If it hands off instead of answering
52+
53+
Every handoff records a reason (in `message.log` and the server logs):
54+
55+
| reason | meaning | what to do |
56+
|---|---|---|
57+
| `no_knowledge` | no source is `ready` | add a source; confirm it reached `ready` |
58+
| `low_confidence` | retrieval scored below threshold | question isn't covered, or an `EMBEDDING_DIM` mismatch — re-ingest |
59+
| `router_escalate` | the router saw no relevant source | expected for genuinely off-topic questions |
60+
| `explicit_request` | the visitor asked for a human | expected |
61+
| `attachment` | the message was image/file-only | expected — the concierge can't read files |
62+
| `ai_error` | a Claude or embeddings call threw | check your keys and the server logs |
63+
64+
## What CI covers
65+
66+
`.github/workflows/ci.yml` builds against a real pgvector service and runs the
67+
migrations on every push/PR — so the schema and type-check are always green. It
68+
does **not** call the AI (no keys in CI); the live check above is yours to run.

0 commit comments

Comments
 (0)