docs(adr): no hand-off and no connector (ADR 0022); several providers and personal keys in ADR 0021
ADR 0022, decided 23 Oct: the hand-off to assistants outside the app and the Claude connector (ADR 0006) go in v0.19.0, app and server together; the field keeps the web search. ADR 0021 takes the developer's D4-D6: the operator's keys for several providers, granted per person; personal keys on the person's devices, only where the operator allows them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
parent
2d51a77f2c
commit
1454fd7bfc
1 changed files with 120 additions and 21 deletions
|
|
@ -1,9 +1,9 @@
|
|||
# ADR 0021: A private instance: approved sign-ups and an assistant the operator grants
|
||||
|
||||
- **Status:** proposed 23 Oct 2026. Decided by the developer on 23 Oct: D1–D3 below; the
|
||||
- **Status:** proposed 23 Oct 2026. Decided by the developer on 23 Oct: D1–D6 below; the
|
||||
rest under *Proposal* is for review before building.
|
||||
- **Builds on:** ADR 0006 (the assistant connector), ADR 0016 (the register), ADR 0019
|
||||
(private studysets), ADR 0020 (SimpleLogin).
|
||||
- **Builds on:** ADR 0016 (the register), ADR 0019 (private studysets), ADR 0020
|
||||
(SimpleLogin), ADR 0022 (the hand-off and the connector gone).
|
||||
- **Changes:** the rule of 10 Oct 2026 that Halkyon calls no language model with an API
|
||||
key, for an instance whose operator opts in.
|
||||
|
||||
|
|
@ -16,8 +16,14 @@ with, and with SimpleLogin (ADR 0020) the instance keeps no addresses anyway.
|
|||
On 10 Oct the developer ruled out API keys for language models: on a public instance anyone
|
||||
could drive up the bill, and each person should decide what a model sees. Assistants came
|
||||
through people's own subscriptions instead: the connector (ADR 0006) and the hand-off to
|
||||
Claude or Lumo. A family instance whose operator approves every account takes away the
|
||||
first reason; the second holds if the app shows what it sends.
|
||||
Claude or Lumo, both gone since 23 Oct (ADR 0022). A family instance whose operator
|
||||
approves every account takes away the first reason; the second holds if the app shows
|
||||
what it sends.
|
||||
|
||||
People in one family may prefer different providers (the developer: "I might use myself
|
||||
the Claude API while other might want to use the OpenAI API, then one other the Proton
|
||||
Lumo API"). A plan for Claude's or ChatGPT's apps is no API access: the APIs are billed
|
||||
apart, by their providers' consoles.
|
||||
|
||||
Two protocols reach nearly every provider: Anthropic's Messages API (Claude), and the
|
||||
OpenAI-compatible chat completions (OpenAI, Mistral, Google Gemini's compatibility
|
||||
|
|
@ -36,6 +42,14 @@ Lumo API for autumn or winter 2026 without publishing its format; community proj
|
|||
- **D3 Individual limits and statistics** (the developer: "so I can see after the fact who
|
||||
potentially drove up the bill"): each person's use is limited and counted, and the
|
||||
operator sees it per person.
|
||||
- **D4 Several providers, all on the operator's keys** (over personal keys alone, and over
|
||||
both, which was recommended): the operator sets up the assistants the instance offers
|
||||
and grants each person one or more.
|
||||
- **D5 Personal keys stay on the person's devices** (recommended, over keeping them on the
|
||||
instance): the app calls the provider itself, and the instance sees neither key nor
|
||||
text.
|
||||
- **D6 Personal keys only where the operator allows them** (over every instance, which was
|
||||
recommended): an instance setting, off unless switched on.
|
||||
|
||||
## Proposal
|
||||
|
||||
|
|
@ -53,29 +67,35 @@ Lumo API for autumn or winter 2026 without publishing its format; community proj
|
|||
waiting at once, and sign-ups per network limited as today's, so strangers can't flood
|
||||
the list. The operator's app shows a badge; there is no mail, as the instance keeps no
|
||||
addresses.
|
||||
4. **The provider**, set by the operator: `ASSISTANT_PROVIDER` (`anthropic`, or `openai`
|
||||
for every OpenAI-compatible service), `ASSISTANT_BASE_URL` (each provider's own
|
||||
unless set), `ASSISTANT_MODEL`, and the key `assistantApiKey` in the passwords.
|
||||
Unset: no assistant on the instance, as today. Switching from Claude to OpenAI or to
|
||||
Lumo is a change of these and a restart; no app changes.
|
||||
5. **Two adapters of our own** behind one interface in the server, over plain HTTP, no
|
||||
SDKs. Features use only what both protocols share: a system prompt and messages,
|
||||
4. **The assistants**, set by the operator (D4): one or more, each with an ID, the name
|
||||
people see ("Claude Sonnet"), its protocol (`anthropic`, or `openai` for every
|
||||
OpenAI-compatible service), its base URL (the provider's own unless set) and model in
|
||||
`deploy/.env` (`ASSISTANTS=claude,gpt`, then `ASSISTANT_CLAUDE_NAME`, `_PROTOCOL`,
|
||||
`_BASE_URL`, `_MODEL`), and its key in the passwords (`ASSISTANT_CLAUDE_KEY`). There
|
||||
rather than in the app: the keys stay out of the database and its hourly backups, where
|
||||
the operator keeps the instance's other secrets. None set: no assistant on the
|
||||
instance, as today. Adding Lumo once its API is out, or switching a model, is a change
|
||||
of these and a restart; no app changes.
|
||||
5. **Two adapters of our own** behind one interface, over plain HTTP, no SDKs, in a pure
|
||||
Dart package of their own, which the server uses for the operator's assistants and the
|
||||
app for personal keys (10). Features use only what both protocols share: a system prompt and messages,
|
||||
streamed text, and tool calls where a feature needs them. Tested against recorded
|
||||
replies of each. Not chosen: LiteLLM as a sidecar (a Python proxy for about 100
|
||||
providers: one more container to run and update, for providers never used) and
|
||||
langchain_dart (a large framework for a small job).
|
||||
6. **Grants** (D2): nobody has the assistant until the operator switches it on for them,
|
||||
with a limit. The person agrees once to what goes where: the provider's name, that the
|
||||
6. **Grants** (D2, D4): nobody has an assistant until the operator grants it to them, one
|
||||
assistant at a time, each grant with its limits. The person agrees once to what goes where: the provider's name, that the
|
||||
instance sees the text on its way, and that the operator sees how much they used,
|
||||
never what.
|
||||
7. **Asking:** the app shows what it will send, the person sends it to the instance
|
||||
7. **Asking:** the field offers the assistants granted beside the web search (the one last
|
||||
used first), the app shows what it will send, the person sends it to the instance
|
||||
(`assistant.ask`, the answer streamed), the instance checks the grant and what is left
|
||||
of the limit, forwards the request, and counts what the provider's reply reports
|
||||
(input and output tokens). Past the limit it refuses (`limitReached`), saying when it
|
||||
resets. A private studyset (ADR 0019) goes only as far as the person sends it,
|
||||
decrypted on the device for that request.
|
||||
8. **Limits and statistics** (D3): per person, per day and per month (a day's limit stops a
|
||||
runaway loop early). Counted per person, day and model: requests, input and output
|
||||
8. **Limits and statistics** (D3): per grant, per day and per month (a day's limit stops a
|
||||
runaway loop early). Counted per person, day and assistant: requests, input and output
|
||||
tokens, and the cost at the prices the operator enters per model (per million tokens;
|
||||
without prices, tokens only, and limits in tokens). Neither prompts nor answers are
|
||||
kept. The operator sees everyone's figures in the app, per person and month; each
|
||||
|
|
@ -83,14 +103,23 @@ Lumo API for autumn or winter 2026 without publishing its format; community proj
|
|||
9. **The privacy page** names the provider, says what is sent and that the instance sees
|
||||
it in passing, that the operator sees each person's figures, and that no content is
|
||||
kept.
|
||||
10. **The connector and the hand-off stay** (ADR 0006): anyone may still use their own
|
||||
Claude or Lumo.
|
||||
10. **Personal keys** (D5, D6), where the operator switches them on (`ASSISTANT_OWN_KEYS`,
|
||||
off unless `on`; `instance.info()` says so, and the app offers them only then): a
|
||||
person adds a provider in Settings (protocol, base URL, model, their key), and may add
|
||||
several. The key goes into their settings, which are sealed with their personal key
|
||||
(ADR 0019), so it reaches their other devices and the instance can't read it. The app
|
||||
calls the provider directly with the same adapters (5); the instance sees neither key
|
||||
nor text and counts nothing, and the person's limits are their provider's. The
|
||||
instance can't stop a modified app from doing so, and needn't: nothing passes through
|
||||
it. Personal assistants are offered in the field beside those granted.
|
||||
|
||||
## Consequences
|
||||
|
||||
- The operator pays the provider, and sees who used how much, never what for.
|
||||
- The instance sees what is asked on its way to the provider; the provider's own terms
|
||||
then apply to it.
|
||||
- A provider that is down stops the in-app assistant, not the hand-off or the connector.
|
||||
- A provider that is down stops the assistants on it; the others granted, personal keys
|
||||
and the web search go on.
|
||||
- Strangers can sign up and wait, but can't use anything, and their accounts lapse.
|
||||
- Public instances keep the rule of 10 Oct: their operators leave the assistant unset.
|
||||
- Public instances keep the rule of 10 Oct: their operators leave the assistants unset,
|
||||
and may allow personal keys.
|
||||
|
|
|
|||
Loading…
Reference in a new issue