AI-powered anonymous 360° peer feedback for professional development.
Smart 360 collects structured anonymous feedback from your team and uses Google Gemini to consolidate it into clear strengths, growth areas, and concrete next steps. It is a single server-rendered Go application — one binary, backed by Postgres, with no separate frontend build.
- Source: https://github.com/mondial7/smart-360
- Architecture decisions:
docs/adr/ - Contributor guide:
CLAUDE.md
- What it does
- How a round works
- Who can do what
- Tech stack
- Run it locally
- Configuration
- Google OAuth setup
- Gemini API setup
- Testing
- Architecture
- Security & privacy
- Contributing & license
Smart 360 runs structured peer-feedback rounds and uses Google Gemini to turn raw answers into something a person can act on. Designed for scaleup engineering / product teams where calibrated, growth-oriented feedback matters more than a pile of platitudes.
- Growth-framed, behaviourally anchored questions — the default template
uses Continue / Block / Amplify / Experiment framing. Question text lives in a
templatestable, so you can ship one template per role family without code changes. - Self-assessment + delta — the subject answers the same questions in first-person; the AI surfaces blind spots, hidden strengths, and aligned themes (the highest-signal output of a 360).
- Voice breakdown — manager, peer, and direct-report inputs are synthesised as distinct streams so a manager's "ready for next level" doesn't average into a peer's "easy to work with".
- Competency rubric (Likert) — templates can declare competencies; reviewers rate 1–5 with a one-line justification. The consolidation shows per-voice averages, spread, and a wide-spread warning for calibration gaps.
- Private-to-manager channel — an optional note on peer submissions feeds a manager-only synthesis, stripped from the subject's view and PDF.
- Dedicated moderation pass — a separate Gemini call scrubs identity-targeted
/ personality-attack / off-topic content before synthesis, recorded in
moderation_logs. - Live consolidation — moderation + synthesis progress is streamed to the browser over Server-Sent Events.
- PDF export — a print-ready PDF of any shared consolidation (manager-only section omitted from the subject's copy).
- Server-rendered analytics — radar and donut charts drawn as SVG on the server, no JS chart library.
- Role-based access + audit log — Admin, Team Admin, Member; every status
transition (
draft → active → closed → shared) is recorded. - Single binary — templates and assets are embedded via
//go:embed.
Reviewer identity is anonymised to the subject (the breakdown shows only relationship + frequency tags); the admin / round creator can see whose submission is whose for accountability and audit.
- Admin (or Team Admin) creates a round — picks the subject, a template, reviewers, and a deadline. The subject participates via self-assessment, not as a reviewer.
- The subject self-assesses — same questions, first-person wording.
- Reviewers submit — each declares their relationship (manager / report / peer / cross-functional) and interaction frequency (daily / weekly / monthly / rarely), answers the questions, rates competencies 1–5 with justifications, and can leave a private manager-only note.
- Round closes — no further submissions.
- Moderation scrub — every submission goes through a Gemini call that strips identity-targeted / personality-attack / off-topic content (logged).
- AI synthesis — a second Gemini call produces the executive summary, top 1–3 focus areas, growth opportunities, per-question summaries, the self-vs- peers delta, the manager / peer / report voice streams, and the manager-only synthesis. Progress streams live over SSE.
- Server-side aggregates — competency averages per voice, spread, and reviewer counts are computed deterministically (not via the AI).
- Admin reviews and shares — sharing flips the round to
sharedand unlocks the consolidation for the subject. - Subject reads — sees everything except the manager-only channel.
Without a GEMINI_API_KEY, moderation is a no-op and synthesis falls back to a
non-AI combine that stitches the raw answers together honestly.
| Role | Can do |
|---|---|
| Admin | Everything — manage users + teams, create any round, view all rounds, generate and share any consolidation. Bootstrapped via ADMIN_EMAIL. |
| Team Admin | Manage their team's members; create and manage rounds. |
| Member | Submit feedback when assigned as reviewer, read consolidations shared with them (in-app + PDF). |
- Go 1.26 ·
net/http+ chi - UI: Go
html/template+ htmx + SSE (server-rendered SVG charts — no SPA, no JS framework) - PostgreSQL 16 via pgx (hand-written SQL)
- Auth: Google OAuth 2.0 → HttpOnly session cookie
- AI: Google Gemini (
generative-ai-go) - PDF:
github.com/go-pdf/fpdf
See the ADR log for why.
git clone https://github.com/mondial7/smart-360.git
cd smart-360
docker compose up -d postgres # start Postgres
cp .env.example .env # DEV_MODE=true is fine for local
go run ./cmd/server # migrates + seeds + serves :8080Open http://localhost:8080. With DEV_MODE=true you can sign in from the login
page's dev-login (or GET /auth/dev-login?email=you@example.com) without Google
credentials. Set ADMIN_EMAIL to your email — that account is the admin.
cp .env.example .env # set SESSION_SECRET; DEV_MODE=false for a real run
docker compose up -d --build # Postgres + the app on :8080The app is a single binary (go build ./cmd/server) with all templates and
static assets embedded.
All configuration is read from the environment (.env in the repo root for local
runs). See .env.example for the annotated list.
| Variable | Required | Default | Description |
|---|---|---|---|
PORT |
No | 8080 |
Listen port. |
APP_URL |
No | http://localhost:8080 |
External base URL. |
DATABASE_URL |
Yes | local compose DSN | Postgres connection string. |
SESSION_SECRET |
Yes (prod) | dev fallback if DEV_MODE |
Signs session cookies. Generate with openssl rand -hex 32. |
ADMIN_EMAIL |
Recommended | — | The user who signs in with this email is the global admin (deterministic bootstrap). |
LOG_FORMAT |
No | text |
text or json (structured logs for a log shipper). |
GOOGLE_CLIENT_ID |
For Google login | — | OAuth client ID. |
GOOGLE_CLIENT_SECRET |
For Google login | — | OAuth client secret. |
GOOGLE_REDIRECT_URL |
No | http://localhost:8080/auth/callback |
Must match the Google Cloud Console redirect URI. |
GEMINI_API_KEY |
No | — | Empty → moderation no-op + non-AI consolidation fallback. |
DEV_MODE |
No | false |
Enables /auth/dev-login, relaxes the Secure cookie flag. Never true in production. |
Security notes
- Generate a real
SESSION_SECRETfor production (openssl rand -hex 32). - Never commit
.env(already gitignored). - OAuth in production requires HTTPS — terminate TLS at a reverse proxy and set
GOOGLE_REDIRECT_URL/APP_URLaccordingly.
- Open Google Cloud Console → Credentials.
- Create credentials → OAuth client ID → Web application.
- Add an Authorized redirect URI:
- Local:
http://localhost:8080/auth/callback - Production:
https://yourdomain.com/auth/callback
- Local:
- Copy the Client ID and Client Secret into your
.env.
- Open Google AI Studio.
- Create API Key and paste it into
GEMINI_API_KEY.
Consolidation works without a key — it just uses the non-AI fallback.
go test ./... # full pyramid (gateway tests need Docker)
go test -short ./... # unit + in-memory handler tests only (no Docker)Three layers: unit (ai, view, auth), in-memory handler integration against
repo fakes, and pgx gateway tests against a real Postgres via testcontainers.
See CLAUDE.md for detail.
Browser ──HTML/htmx/SSE──▶ Go server (single binary)
├─ chi router + html/template + embedded assets
├─ auth: Google OAuth → session cookie + CSRF
├─ ai: Gemini moderation + synthesis (SSE progress)
├─ pdf: fpdf consolidation renderer
└─ repo: pgx ──▶ PostgreSQL
Requests hit handlers (internal/handlers) → repositories (internal/repo) →
render via the view Renderer (internal/view). The ai, pdf, and chart
packages are pure (no HTTP/DB) and unit-tested in isolation. See
CLAUDE.md for conventions and docs/adr/ for
the decision log.
The product handles sensitive workplace feedback. How data is stored, who can
see it, and what is sent to Google Gemini are documented in
SECURITY.md.
- Fork and create a feature branch (
feat/your-thing). - Run
go test ./...andgo vet ./.... - Use conventional commit prefixes (
feat:,fix:,docs:, …). - Record significant architecture decisions as a new ADR in
docs/adr/.
Licensed under the MIT License.