Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ SMTP_PASS=
JOB_EMAIL_COOLDOWN_MINUTES=60
MAIL_FROM=fledge@hugoxk.online
ADMIN_EMAIL=admin@hugoxk.online
# Optional. Feedback and unexpected-error reports go here; empty falls back to ADMIN_EMAIL.
FEEDBACK_EMAIL=

# ── Platform model gateway ────────────────────────────────────────────────
# Any OpenAI-compatible /v1 endpoint. Keep the real endpoint out of git.
Expand All @@ -62,6 +64,16 @@ USD_CNY_RATE=7.2
# balance may not cover a first job and recommends an own model. 50 CNY default.
PLATFORM_QUOTA_COMFORT_CNY=50

# Feedback triage: route new feedback to the server FlyBot's Fledge Lead.
# Prefer a repo Fledge already manages:
FLEDGE_LEAD_REPO_ID=
# Or target an externally managed FlyBot bridge:
FLEDGE_LEAD_BRIDGE_URL=
FLEDGE_LEAD_BRIDGE_TOKEN=
FLEDGE_LEAD_SESSION_ID=
# The sanitized public issue target the Lead may write to.
FEEDBACK_PUBLIC_REPO=HugoXK/fledge

# ── Service directory (console links) ─────────────────────────────────────
# Point these at your own deployments if you self-host the sibling services.
PORTAL_URL=https://portal.hugoxk.online
Expand Down
1 change: 1 addition & 0 deletions changelog.d/796-feedback-error-report.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Settings now has a Feedback card that stores the note, audits it and mails the maintainer address (`FEEDBACK_EMAIL`, falling back to `ADMIN_EMAIL`), plus an About card and footer link to star the public GitHub repository. An unexpected HTML 5xx page now offers “Report to the maintainer” after confirming the data-access consent: the server keeps a pathname-only, redacted error record with a redacted stack and a random reference and mails it once; a 4xx page never offers the button and no request body, query, cookie or token is stored.
1 change: 1 addition & 0 deletions changelog.d/814-feedback-lead.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Feedback and unexpected-error reports now go to the server FlyBot's Fledge Lead through a repo already connected to Fledge (`FLEDGE_LEAD_REPO_ID`) or a direct bridge (`FLEDGE_LEAD_BRIDGE_URL/TOKEN/SESSION_ID`). A per-ref signed triage token, expiring after seven days, with a bounded five-attempt delivery outbox, lets the Lead read the redacted record and write a user-facing decision back through the triage API; statuses carry their required message/issue/PR/version fields and a retry is a no-op; the reporter gets an in-app and email update for a user problem, a duplicate, a confirmed issue, a fix with its issue/PR and version, and a final “now live” notice when the running version catches up. The Lead gets a ready-to-use sanitized issue draft; a public GitHub issue may @ the reporter only after a separate sanitized-publication consent, and no raw message, stack, email or token is published by Fledge. Settings also lists the reporter's own feedback status, links and fixed/released version.
25 changes: 25 additions & 0 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,31 @@ header, not a section — so at the top of that page nothing has passed and
`#release`, the first chip, is marked. That fallback is the accepted state, not a
defect.

`/settings` also carries a Feedback card and an About card. The feedback form
posts to `/feedback`, stores a `feedback` row and mails the address in
`FEEDBACK_EMAIL` (falling back to `ADMIN_EMAIL`); the About card and the footer
link to the public GitHub repository with a Star call-to-action. An unexpected
HTTP 5xx creates an `error_reports` row with a random `ref` before the error page
is rendered; the page shows the data scope, requires the consent checkbox, then
offers one POST to `/errors/:ref/report`, which claims the row atomically and
mails the same address. The error row keeps only method, pathname, status, error
name, redacted message, redacted stack, version, commit, the signed-in login and
the consent version/time — never the query, body, headers, cookies or a token.
Both routes carry a route-level limiter, and a 4xx never offers the report button.

The feedback lifecycle is one triage row per ref (`feedback_triage`) plus
`feedback_events`. New reports are sent to the server FlyBot's Lead through a
connected repo (`FLEDGE_LEAD_REPO_ID`) or a direct bridge; the packet carries a
per-ref signed token, not the raw stack. The Lead reads and writes through
`GET`/`POST /api/feedback/:ref` and `/triage`; the read response carries a
sanitized issue draft, every transition appends an event, and the signed-in
reporter sees in-app/mail updates plus a Settings history row. Delivery is a
bounded outbox: a failure is retried with backoff up to five attempts and the
row records the last error/next attempt; a delivered row is never resent. A `fixed_version` at or
below the running `VERSION` sends the fixed notice once; the startup scan then
marks the record `released` and sends the “now live” notice once. Fledge never
creates the public issue: the Lead does, sanitized, and records its URL.

The dashboard's Repositories card owns the full content row; its Add repository
button sits in the card header and opens a native `<dialog>` with the GitHub App
and fine-grained token options. `#add-repo` remains the deep link used by the
Expand Down
4 changes: 4 additions & 0 deletions docs/deploy.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ Key settings:
| `MAIL_TRANSPORT` | `smtp` in production, `console` for a local demo |
| `SMTP_HOST/PORT/SECURE/USER/PASS` | For yeah.net: `smtp.yeah.net`, 465, secure |
| `MAIL_FROM` / `ADMIN_EMAIL` | `fledge@hugoxk.online` / `admin@example.com` |
| `FEEDBACK_EMAIL` | Optional feedback/error-report destination; defaults to `ADMIN_EMAIL` |
| `FLEDGE_LEAD_REPO_ID` | Repo connected to Fledge whose FlyBot Lead triages feedback |
| `FLEDGE_LEAD_BRIDGE_URL/TOKEN/SESSION_ID` | Direct bridge target when the Fledge Lead is external |
| `FEEDBACK_PUBLIC_REPO` | Sanitized public issue target; defaults to `HugoXK/fledge` |
| `PLATFORM_LLM_BASE_URL` | OpenAI-compatible `/v1` endpoint |
| `PLATFORM_LLM_KEY` | Platform key; never commit it |
| `INITIAL_QUOTA_CNY` | Default 1 |
Expand Down
1 change: 1 addition & 0 deletions docs/secrets.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ predates this inventory — it is not a value.
| `SMTP_PASS` | approval and digest mail | production `.env` | provider's send-only SMTP credential | owner | unknown | mint a new SMTP credential at the provider | `npm run mail:outbox` on a development box, or one approval mail | unknown |
| `PLATFORM_LLM_KEY` | platform-funded model calls | production `.env` | one OpenAI-compatible key with a spend cap | owner | unknown | mint at the provider, then replace and restart | one platform-funded job reaches the gateway | unknown |
| `FLYBOT_BRIDGE_TOKEN` | shared secret on the optional global bridge | production `.env` and the bridge host | random 32 bytes | owner | unknown | `openssl rand -hex 32` in both places | one mock job round-trips a callback | unknown |
| `FLEDGE_LEAD_BRIDGE_TOKEN` | bearer token for the external Fledge Lead triage bridge | production `.env` and the bridge host | random 32 bytes | owner | unknown | `openssl rand -hex 32` in both places | one feedback packet reaches the configured Lead session | unknown |
| `GITHUB_APP_PRIVATE_KEY_PATH` | GitHub App authentication | file on the host, path in `.env` | App private key | owner | unknown | regenerate the key in the App settings and replace the file | install the App on a repository and list its repositories | unknown |
| `GITHUB_APP_WEBHOOK_SECRET` | verifies App webhooks | production `.env` | random 32 bytes | owner | unknown | `openssl rand -hex 32` in both the App and `.env` | redeliver one webhook and confirm it is accepted | unknown |
| `INBOUND_EMAIL_SECRET` | shared secret with the inbound Email Worker | production `.env` and the worker | random 32 bytes | owner | unknown | `openssl rand -hex 32` in both places | reply to one digest mail and confirm it lands | unknown |
Expand Down
38 changes: 38 additions & 0 deletions docs/security.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,44 @@ classifying sweep, the ADR and the remaining privileged routes. A route added
tomorrow inherits nothing from this section, and the eight `config.rateLimit`
handlers above are unchanged by it — same window, same key.

### Feedback and unexpected-error reports

The settings Feedback card stores one `feedback` row (user, kind, redacted
message, optional page) and mails `FEEDBACK_EMAIL`, which falls back to
`ADMIN_EMAIL` when unset. The user sees the same bilingual page and a flash
notice; the audit row records only the id, kind and character count.

The error handler writes an `error_reports` row only for HTML 5xx responses. The
row carries a random `ref`, method, pathname, status, error name, redacted and
truncated message, a redacted and truncated stack, version, commit, and the
signed-in login when there is one. It never carries the query string, request
body, headers, cookies, email address or token. The error page shows what the
report contains and requires a consent checkbox before it exposes the `ref` as a
capability: one POST to `/errors/:ref/report` marks it reported atomically,
records the consent version and time, and mails the maintainer address. The route
is usable without a session because the error may happen before sign-in, is
rate-limited by client address, and a second click does not send a second mail.
Without the consent value the row stays `captured` and nothing is mailed. A 4xx
refusal is user-caused and never offers the button.

### Feedback triage

Each feedback/error record gets a per-ref triage token derived from
`HMAC(SESSION_SECRET, "feedback-triage:<ref>")`. The token is delivered only in
the private packet to the Fledge Lead; the read and write endpoints accept it as
a bearer capability for that one ref, it expires after seven days, and both are
rate-limited. Each status has required fields (a user message, an issue URL, a
pull URL, a fixed version), public links must be issues/pulls in
`FEEDBACK_PUBLIC_REPO`, and a replay of the same decision is a no-op. The Lead may read the
redacted message/stack and write a conclusion, but Fledge does not publish raw
feedback or error data. A public GitHub issue is created by the Lead only after a
separate sanitized-publication consent, and the issue may @ the reporter only
when that consent is present. The triage read returns a ready-to-use sanitized
issue draft built from `public_summary`; raw message/stack never enters it. The triage row stores the issue/PR URLs, fixed and
released versions, user-facing message, public summary, notification state and
the bounded Lead-delivery attempts;
the raw text remains in the feedback/error row.

### CSRF

Every state-changing browser form includes a hidden `csrf` field derived from
Expand Down
Loading
Loading