Model endpoint and telemetry¶
Which model Winner AI calls, how to point it at a hosted OpenAI-compatible
provider or Amazon Bedrock instead of the compose Ollama, how to check that model with
fleetview-model-check before Prompt Coaching is switched on, what each feature
sends to that model, and whether the stack sends telemetry. It assumes a
server installed from the bundle (Install the server).
On an installation from the bundle, read the server's commands as
Commands on the other pages
says: in <install>, as the user who owns <install>/.env, with plain
docker compose and no sudo.
What calls a model¶
| What calls it | Chosen by | Default |
|---|---|---|
| The jobs passes behind Prompt Coaching, Skills and Benchmarks; the control plane uses the same values (Settings, and Skills' Generate) | FLEETVIEW_LLM_ENDPOINT, FLEETVIEW_LLM_MODEL, FLEETVIEW_LLM_API_KEY, and for Amazon Bedrock FLEETVIEW_LLM_PROVIDER and FLEETVIEW_LLM_REGION |
the compose Ollama at http://ollama:11434/v1, qwen3:4b-instruct-2507-q4_K_M, no key, provider openai |
| Generated insights in the dashboard | INSIGHTS_ENDPOINT, INSIGHTS_MODEL, OPENAI_API_KEY |
the same compose Ollama and model |
| The dashboard's query embeddings and the bootstrap push | nothing; fixed | the compose Ollama, nomic-embed-text, 768 dimensions |
| Each laptop's own embeddings | nothing; handed out at enrollment | the laptop's http://localhost:11434/v1, nomic-embed-text, 768 dimensions |
The first two rows are what this page changes. The last two are under Embeddings.
What the endpoint must support¶
This section is for the default provider, an OpenAI-compatible endpoint
(FLEETVIEW_LLM_PROVIDER=openai). Amazon Bedrock has its own section.
- OpenAI-compatible chat completions. Each call is
POST <endpoint>/chat/completions, so the endpoint is the provider's base URL, the one that ends in/v1. - Strict structured output:
response_formatof typejson_schemawithstrict: true. A provider that does not support it answers every call with an error, and the model check below fails. - A bearer key. The key is sent as
Authorization: Bearer <key>. A blank key sends no header. - https.
FLEETVIEW_LLM_ENDPOINTmust be https or a compose-internal host. Anything else is refused withFLEETVIEW_LLM_ENDPOINT must be https or a compose-internal host; refusing to use <scheme>://<host>[:port]. For a jobs pass the refusal is theerroron itspipeline_runsrow; Skills' Generate shows that the host's model endpoint is misconfigured.INSIGHTS_ENDPOINTmust be https too: the Dashboard refuses any http endpoint other than the compose Ollama,http://ollama:11434.
The client retries rate limits and transient errors itself, honouring
Retry-After, and adapts to providers that reject temperature or want
max_completion_tokens, so do not work around those.
Amazon Bedrock is a separate provider, not an endpoint of this kind: its OpenAI-compatible endpoint is not used, and the host model reaches Bedrock through its own API, described in the next section.
Amazon Bedrock¶
An operator can run the host model on Amazon Bedrock. The control plane calls Bedrock's Converse API and signs each request itself, so there is no gateway, SDK or extra container. It applies to the host model only: a team's own model stays OpenAI-compatible, and embeddings and the dashboard's insights are unchanged. Prompt Coaching and Skills need nothing else; Benchmarks also needs its Routes set to Bedrock ids (below).
- Three settings.
FLEETVIEW_LLM_PROVIDER=bedrock,FLEETVIEW_LLM_REGION(an AWS region such asus-east-1) andFLEETVIEW_LLM_MODEL, which must be a Bedrock model or inference-profile id: the default model is Ollama's. LeaveFLEETVIEW_LLM_ENDPOINTblank, and the endpoint is derived ashttps://bedrock-runtime.<region>.amazonaws.com. Set it only for a VPC endpoint: a Bedrock endpoint must be an https Bedrock runtime host (bedrock-runtime.<region>.amazonaws.comor its VPC interface endpoint), so the control plane andscripts/check-env.shrefuse a hosted OpenAI-compatible endpoint left from before rather than send it the Bedrock API key or a signed request. Settings shows the provider and region read-only. - Credentials, in this order. A Bedrock API key in
FLEETVIEW_LLM_API_KEY, sent as a bearer token; else the AWS variablesAWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEYandAWS_SESSION_TOKENin the process's environment, which the compose file does not pass, so nothing in the deployed stack uses them; else the instance role of the EC2 host, read through IMDSv2 and refreshed before it expires. Nothing is read until the first call. - The instance role needs hop limit 2. A container reaches the metadata
service one network hop further than the host does, so the instance's
metadata hop limit must be 2 for the role to reach it (Network).
The trade-off is that every container on the host can then use the role, so
the role should allow only
bedrock:InvokeModelon the chosen model and inference-profile ARNs, plus the DNS mode's TXT write where that mode is used. A Bedrock API key avoids the role and the hop limit. - The first call on a schema is slow. Bedrock compiles each structured output schema on its first use, which AWS documents as taking up to about 90 seconds, and the default 120 s call timeout covers it. AWS caches the result per account for 24 hours from that first use, so the slow call recurs after that.
- Errors. A failed call records the HTTP status and AWS's error type, for
example
HTTP 429 ThrottlingException, and never the message. A host with no credentials from any of the three sources fails the pass with a fixed reason, without sending a request. - Routes must be Bedrock ids. The release-default Benchmarks Routes'
candidates (
claude-sonnet-5under theclaude-opusandclaude-fablekeys,gpt-5.5undergpt-5.6) are not Bedrock model or inference-profile ids, so every replay that is not local answers 400 until the operator or the team sets Routes that name ids Bedrock serves. - Check it first.
fleetview-model-check(next sections) builds its client the same way. It runs in the jobs container, which gets no AWS variables, so there it uses the Bedrock API key or the instance role. Itsrefused:line can also name the region, or an endpoint that is not an https Bedrock runtime host. - Tried so far: no Claude model on Bedrock has passed the model check yet.
On 2026-10-29,
us.anthropic.claude-sonnet-4-6in us-east-1 answered the calls, with thinking off andtemperatureboth accepted. It did not pass the rule below: Model fit F1 was 0.76 against a baseline of 0.77, andanyF1 0.70 against 0.78, mostly because it over-flagged Context. The run took 11 calls and 29,983 + 7,638 tokens, about USD 0.20. The same day Claude Sonnet 5 (us.anthropic.claude-sonnet-5) was refused for that account withAccessDeniedException, so it has not been measured. Run the model check on your own model before you turn Prompt Coaching on.
An example in us-east-1¶
The host .env for Bedrock in us-east-1, with the instance role and no key:
FLEETVIEW_LLM_PROVIDER=bedrock
FLEETVIEW_LLM_REGION=us-east-1
FLEETVIEW_LLM_MODEL=us.anthropic.claude-sonnet-4-6
FLEETVIEW_LLM_ENDPOINT=
FLEETVIEW_LLM_API_KEY=
- Claude models on Bedrock are called through an inference profile, so the
model is a profile id such as
us.anthropic.claude-sonnet-4-6(routed in the United States) orglobal.anthropic.claude-sonnet-4-6. Runaws bedrock list-inference-profiles --region us-east-1to see which profiles the account has. - The Sonnet 4.6 live runs, and the policy below, used a
us.profile only; the one call made on aglobal.profile, for Sonnet 5, was refused for the account. - For a VPC interface endpoint, set
FLEETVIEW_LLM_ENDPOINTto its https runtime host instead of leaving it blank. - To use a Bedrock API key instead of the role, put it in
FLEETVIEW_LLM_API_KEY. - Then recreate the services, as in step 3 of Point the stack at a hosted model.
The instance role's policy¶
The role needs one statement, bedrock:InvokeModel, on the inference
profile and on the foundation model in every region that profile routes to.
Nothing broader: at hop limit 2 every container on the host can use this
role.
-
Read the ARNs from AWS rather than typing them:
aws bedrock get-inference-profile --region us-east-1 \ --inference-profile-identifier us.anthropic.claude-sonnet-4-6 \ --query '[inferenceProfileArn, models[].modelArn]' -
Put them in the policy. This is the policy the live run used, with
<ACCOUNT_ID>in place of the account:{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "bedrock:InvokeModel", "Resource": [ "arn:aws:bedrock:us-east-1:<ACCOUNT_ID>:inference-profile/us.anthropic.claude-sonnet-4-6", "arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-sonnet-4-6", "arn:aws:bedrock:us-east-2::foundation-model/anthropic.claude-sonnet-4-6", "arn:aws:bedrock:us-west-2::foundation-model/anthropic.claude-sonnet-4-6" ] } ] } -
Converse is authorised by
bedrock:InvokeModel; no other action is used. - In the
dnscertificate mode, the same role also carries that mode's Route 53 statements, which every container can then use too. To keep them from the containers, use a Bedrock API key and leave the hop limit at 1; the key alone does not stop a container reaching the role. - On EC2 the instance needs IMDSv2 required and hop limit 2, set before the
install:
aws ec2 modify-instance-metadata-options --instance-id <instance-id> --http-put-response-hop-limit 2 --http-tokens required. - In the live run, a container at hop limit 2 read the role and Bedrock
accepted the signed call. At hop limit 1 the pass failed with the fixed
reason:
set FLEETVIEW_LLM_API_KEY to a Bedrock API key, set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY, or give the instance a role with metadata hop limit 2.
What the AWS account needs¶
Each of these is set in AWS, not in Winner AI, and a missing one shows as an error on the first call:
- Access to the model. Anthropic models need the account's first-use
details for Anthropic and the model's Marketplace agreement.
aws bedrock get-foundation-model-availability --model-id <model>shows the agreement and the authorisation. - A quota in the region. In Service Quotas, under Amazon Bedrock, the applied tokens-per-minute quota for the model's cross-region (or global) inference must be above 0.
- On 2026-10-29 a model whose availability read available and authorised,
with a quota of 0, was still refused with
AccessDeniedException. - Only AWS can raise a quota of 0: a quota increase or AWS Sales.
- A new account's daily token allowance. AWS limits a new account's tokens per day, and a call over it is throttled until the next day. A full model check took about 38,000 tokens on Sonnet 4.6.
What each error means¶
A failed call records the HTTP status and AWS's error type, never AWS's
message. A pass stores it on its pipeline_runs row, which Skills, Coaching and
Settings show a Manager with when a call last worked, and the model check
prints the model call failed: followed by:
| Recorded | Means | Check |
|---|---|---|
HTTP 403 AccessDeniedException |
The role's policy does not cover the profile or a foundation-model ARN, or the account may not use the model | The policy above. Then the model's availability and its applied quota (What the AWS account needs) |
HTTP 403 with another type, such as UnrecognizedClientException or InvalidSignatureException |
The credentials themselves were refused | The key, or the role and its session |
HTTP 429 ThrottlingException |
The model's quota in the region, or the account's daily allowance | Service Quotas; a daily allowance resets the next day. The client already retries with backoff, so do not add retries |
HTTP 400 ValidationException |
Bedrock refused the request or the schema, or the model id does not exist in the region | The model id and the region. The client already handles the three refusals it knows, once each and without failing: thinking, temperature and too high a maxTokens |
AccessDeniedException looks the same whether the policy or the account is
at fault, and nothing the stack records or prints tells the two apart. Check
the policy first, then the account (What the AWS account needs).
Benchmarks Routes on Bedrock¶
Every candidate in FLEETVIEW_BENCHMARK_ROUTES is sent to Bedrock as the
model, except one that matches FLEETVIEW_BENCHMARK_LOCAL_ROUTES (qwen3
by default), which goes keyless to the compose Ollama. Each of the others
must be an inference-profile or model id that Bedrock serves and that the
role's policy covers. For example, on one line:
FLEETVIEW_BENCHMARK_ROUTES={"claude-opus": ["us.anthropic.claude-sonnet-4-6"], "claude-fable": ["us.anthropic.claude-sonnet-4-6"]}
- The keys are the release defaults' Claude families. A Route under a
family with no entry, such as the default
gpt-5.6, has no candidate on Bedrock. - Add each further profile's ARNs to the role's policy.
- Benchmarks was not run on Bedrock in the live runs, so this example has not been tried.
Point the stack at a hosted model¶
The recommended path is one model for the whole deployment, set in the host
.env.
-
In
<install>/.env, set the threeFLEETVIEW_LLM_*values, and the three insights values to the same provider:FLEETVIEW_LLM_ENDPOINT=https://<provider-host>/v1 FLEETVIEW_LLM_MODEL=<model-id> FLEETVIEW_LLM_API_KEY=<key> INSIGHTS_ENDPOINT=https://<provider-host>/v1 INSIGHTS_MODEL=<model-id> OPENAI_API_KEY=<key>Keep
local-llminCOMPOSE_PROFILES: the compose Ollama still serves embeddings (Embeddings, below). Leavecoachingout ofFLEETVIEW_FEATURESuntil the model passes the model check (next section). The default is[], so a stock deployment has no feature on. -
Set the Benchmarks Routes. The default candidate list includes
qwen3:4b-instruct-2507-q4_K_M, sent keyless to the compose Ollama, and the Ollama container pulls a chat model only while one of the two chat endpoints ishttp://ollama:11434. Once both are hosted and the container is recreated (step 3), it stops pulling that model; set the Routes so that Benchmarks does not depend on it. Prompt Coaching's Model fit suggests its cheaper models from the same map, so set it even if only Coaching is on. SetFLEETVIEW_BENCHMARK_ROUTESto a JSON map whose candidates are model ids your provider serves, on one line:FLEETVIEW_BENCHMARK_ROUTES={"claude-opus": ["<cheaper-model-id>"], "gpt-5.6": ["<cheaper-model-id>"]}- A key matches the original Route's name, or a dash- or dot-separated
prefix of it. Each candidate is sent verbatim as
model. The keys above are the release defaults' families; use your own originals. - Any candidate whose id starts with
qwen3still goes keyless to the compose Ollama, becauseFLEETVIEW_BENCHMARK_LOCAL_ROUTESmatches by prefix. When a hosted candidate starts withqwen3, also setFLEETVIEW_BENCHMARK_LOCAL_ROUTES=[]. - A team that saved its own Routes to try on Settings keeps them; this value applies to the teams that have not.
- A key matches the original Route's name, or a dash- or dot-separated
prefix of it. Each candidate is sent verbatim as
-
Recreate the services that read these values,
ollamaamong them:# in <install> docker compose up -dExpected: compose recreates each service whose values changed,
ollamaincluded, and reports it started. The edits stay in place across releases: an upgrade from the bundle carries.envacross (Upgrade and roll back). -
Check: on a manager's Settings, the group "Model ยท bring your own key" shows the Endpoint and Model rows with the new values.
Per team, instead. A manager can give their organisation its own
endpoint, model and key on Settings; that needs FLEETVIEW_SETTINGS_KEYS
(The settings key). A team endpoint must be
https and a public address: a local name or a private address is refused,
the compose ollama aside. The model check below measures only the host
model in FLEETVIEW_LLM_*, never a team's. The host's own
FLEETVIEW_LLM_API_KEY is never sent to a team's endpoint: a team uses its
endpoint, model and key together, or the host's three together.
FLEETVIEW_LLM_FALLBACK decides what a team with no key of its own gets:
host, the default, lends it the host model; refuse makes no model call
for it at all. Its passes still do the work that needs no model, and the
product pages say No model configured. A deployment whose teams bring their
own key sets refuse, so the host's key and budget are never spent on them.
The settings key¶
A team's model key is stored sealed with FLEETVIEW_SETTINGS_KEYS, so a
database dump or backup alone does not reveal it. A server without that
value cannot read a stored key, and every model pass for that team records
the reason and does only the work that needs no model. Keep the value in
.env only, never beside the backups. Generate one with the running
backend, in <install>:
docker compose exec -T backend \
uv run --no-dev python -c 'from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())'
The value is a comma-separated list: the first key seals, every key listed opens. Rotate without a gap:
- Put the new key first:
FLEETVIEW_SETTINGS_KEYS=<new>,<old>. - Recreate the two services that read it:
docker compose up -d backend jobs. - Re-seal every stored key under the new one:
docker compose exec -T jobs uv run --no-dev fleetview-jobs rekey. It prints how many keys it re-sealed and how many a Manager replaced or removed while it ran, which it leaves as they are; if any stored key opens with neither value it changes nothing and says so. - Drop the old key (
FLEETVIEW_SETTINGS_KEYS=<new>) and recreatebackendandjobsagain.
A lost settings key cannot be recovered: each team's Manager enters its key again on Settings.
Check the model before Prompt Coaching¶
fleetview-model-check runs the production coaching judge over the
labelled set the release ships, against the host's model: the endpoint,
model and key in FLEETVIEW_LLM_*. On Bedrock it also reads
FLEETVIEW_LLM_PROVIDER=bedrock and FLEETVIEW_LLM_REGION, and the endpoint
may be left blank, since the region's own is derived. It scores the answers against the
labels and compares them with the baseline recorded for that release. It
runs inside the jobs container, from the image the server already runs,
so it needs no checkout of the release; recreate jobs first if you have
just changed FLEETVIEW_LLM_*.
# in <install>
docker compose exec -T jobs uv run --no-dev fleetview-model-check
An optional smoke run first: add --limit 10 to judge only the first ten
rows. It proves the endpoint accepts the calls and shows what a call costs,
and it is never a verdict, whatever the number: it ends not a verdict
and exits 3.
The last line is the verdict, and the exit code says the same:
| Last line | Exit | Means |
|---|---|---|
verdict: pass |
0 | A full run, compared with a baseline, neither score more than 0.05 below it |
verdict: fail - ... |
1 | A score more than 0.05 below the baseline, or no baseline at this rubric version for this labelled set |
verdict: fail |
1 | The run did not finish; the line directly above says why, and the cost line above that what it spent. A refused: line has no cost line above it: no call was made |
not a verdict: limited to ... |
3 | A --limit run |
not a verdict: the limited run did not finish |
1 | A --limit run that did not finish; the lines above it read as for verdict: fail |
A usage error exits 2.
The model passes when neither its Model fit F1 nor its any F1 is more
than 0.05 below the newest full-run baseline recorded at the same rubric
version for the exact labelled set this release ships. A score above the
baseline, however far, is a pass. Only the host model is measured, never a
team's own, and only the coaching judge: Skills and Benchmarks call the same
model but nothing here measures them.
A full run prints this shape, with the run's and the baseline's own values in place of the placeholders:
model check: <model-id> (hosted endpoint), rubric v<N>, <rows> of <rows> rows
coaching eval: rubric v<N>, <model-id> (hosted), <date>, <judged> rows (<excluded> excluded), <n> calls, <p>+<c> tokens
...
cost: <n> calls, <p> prompt + <c> completion tokens
vs baseline <baseline-model> <baseline-date> (rubric v<N>, tolerance 0.05)
Model fit f1 0.xxx vs 0.xxx (+0.xxx)
any f1 0.xxx vs 0.xxx (+0.xxx)
verdict: pass
Between the header and the cost line it prints the per-category precision,
recall and F1 table, the starts_task agreement, how many of the judge's
rewrites the faithfulness check dropped, and the rewrite judge's
agreement. The cost line is what the run cost in calls and tokens; a full
run is about a dozen calls of up to ten prompts each.
What it prints is scores, counts, calls and tokens. It prints no prompt and nothing the model or the endpoint wrote, and writes no file. The one log line it can add, on standard error, is the judge's warning that an item was answered twice even when asked again, naming a labelled row's session id and ordinal and no text. When the run does not finish:
refused: FLEETVIEW_LLM_ENDPOINT must be https or a compose-internal hostmeans the endpoint would carry the key in the clear; it is refused before anything is sent, as the stack refuses it.the model call failed: HTTP <status> ...means the endpoint refused the calls: for example a 401, or a 400 from one with no strictjson_schemasupport. What the endpoint said beyond the status is not printed (see below); send it a minimal request with the same model andresponse_formatusingcurlto read it.the model left items unanswered after a second askmeans the model did not answer every item, even when asked again.the run stopped on an unexpected <Type>means the run failed for a reason it does not describe; only the error's type is printed.- A call that runs past
FLEETVIEW_LLM_TIMEOUT_SECONDS(120 by default) fails the run. A CPU-bound local Ollama answering ten prompts at a time can take longer than that, and a full run keeps it busy for every call; give one run more time by adding-e FLEETVIEW_LLM_TIMEOUT_SECONDS=900afterexec -T.
If the model does not pass, leave Prompt Coaching off and try another model.
Switch Prompt Coaching on¶
- Add
coachingtoFLEETVIEW_FEATURES, for exampleFLEETVIEW_FEATURES=["coaching"]. Skills and Benchmarks have their own slugs,skillsandbenchmarks. A team can switch a feature off on Settings but never on: the operator's list is the ceiling. -
Recreate
backendandjobs:# in <install> docker compose up -d backend jobs -
The recreated jobs service sweeps as it starts, so there is no need to wait for the timer (900 seconds by default). Wait until its log shows the sweep finished, then read the newest coaching rows:
# in <install> docker compose logs jobs | grep -E 'rows written this sweep|jobs sweep failed' docker compose exec -T db sh -c 'psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "$1"' sh \ "SELECT feature, model, error, finished_at FROM control.pipeline_runs WHERE feature = 'coaching' ORDER BY started_at DESC LIMIT 5"The database user and name are read inside the
dbcontainer, as compose parsed them from.env, so a quoted value in.envreads the same as it does to Postgres.Expected: the
logscommand printsjobs: <n> rows written this sweep, <m> passes had no model to call; if it prints nothing, the sweep is still running, so run it again.jobs sweep failed for <organisation>means that organisation's passes raised and the sweep carried on;jobs sweep failedwith nothing after it means the whole sweep crashed. Either way, read the traceback below the line withlogs jobs. Then the newestcoachingrow has an emptyerror, and itsmodelis the configured model once the pass judged something;modelis empty when there was nothing new to judge.
Embeddings¶
The compose Ollama stays on while local-llm is in COMPOSE_PROFILES. It pulls
nomic-embed-text at every start and serves the dashboard's query
embeddings and the bootstrap push. That model is fixed in
deploy/pgserve/config.toml, not a compose setting, and it must be the
laptops' model too, because vectors from different models do not compare.
A hosted embedding model is therefore not an option.
Enrollment points each laptop at its own http://localhost:11434/v1 with
nomic-embed-text, 768 dimensions. Winner AI installs neither Ollama nor
the model on a laptop. A laptop without them still pushes its sessions, but
no vectors, and Skills reads only the vectors laptops push, so that
laptop's prompts are absent from Skills. To give a laptop vectors,
install Ollama on it and pull the model: ollama pull nomic-embed-text.
What each feature sends to the model¶
This is what a model endpoint receives from Prompt Coaching, Skills and Benchmarks, per call. An automated test per row captures the request, from the feature's own read but for Skills' Generate, whose test starts from what the mining read returns, and holds the kinds of data listed here and the shape of the request: it fails when one of them starts or stops being sent, or when the request gains a field or a message. It cannot rule out a kind of data nobody thought to plant in its fixtures.
A prompt is sent with nothing redacted, so it carries whatever the person
typed or pasted into it: a path, a name, a file's content. Prompt Coaching
and Skills first cut the blocks an agent adds around a prompt (Claude Code's
hook, command, reminder and message wrappers, Codex's context items and
image wrapper, and the Codex app's browser context, heartbeat block and
writing-block edits, with the request heading it puts before the person's
words) and send what is left; a prompt that is nothing but such a block, or
opens with a hook, command, reminder or task block, is not sent, and neither
is a Codex app scheduled automation run that opens with its Automation: header,
a prompt one agent wrote for another (Codex's agent history, a plan handed to
a fresh context, Claude Code's background task notice), one with a
$ARGUMENTS line, one with a wrapper's closing tag that opens no block, or one
with a wrapper block that is never closed.
The cut finds a block where its opening tag starts a line: one that begins
in the middle of a line, follows another block's closing tag on the same
line, or sits inside a code fence stays in what is sent.
Benchmarks replays a prompt whole, and a prompt a hook or template wrote can
inline an earlier reply or a diff; naming a pattern sends what a person
typed of its prompt, what its shown text would show of a known opening,
and otherwise the prompt as stored.
| Call | Sent |
|---|---|
| Prompt Coaching, judging prompts | Each prompt a person typed, cut to what they typed: at least 40 characters of it, from a prompt of at most 8,192, nothing redacted. Beside each one: the agent's name, the model its session mostly ran on, whether it is the session's first prompt, the team's Routes to try for that model, the session's outcome, and the counts stored with the session (user messages, tool failures, edit churn, and whichever prompt-quality counters are not zero). The agent, model, outcome, Routes and frontier families are sent as plain names: words of letters, digits and . _ - : / @, at most 64 characters, so up to that much of what a laptop reported as its agent, model or outcome sits beside the prompt as ordinary words. Two Routes that come to the same plain name are numbered. The instructions name the team's frontier model families. One call carries the prompts attributed to one member only, up to ten; a session nobody is attributed to yet is sent on its own. |
| Prompt Coaching, the main points to improve | Per person, and once for the sessions nobody is attributed to yet, from what the judge stored and nothing read again from a transcript: how many prompts were judged, were eligible and had findings, how many sessions were judged and had a Model fit verdict, and how many findings each category has; the judge's reasons, up to 30 per category; the cheaper Route it named; and up to 8 of its suggested rewrites, each cut to 600 characters. A rewrite restates the person's prompt, so it carries what that prompt said. |
| Skills, naming a workflow | Up to 5 prompts of the workflow, each cut to what a person typed: at least 50 characters of it, from a prompt of at most 8,192. |
| Skills, Generate and Regenerate | Up to 10 distinct typed prompts of the workflow, each cut to what a person typed and then to 1,800 characters; the workflow's stored name and summary, which the model wrote from its prompts when it was named; how many prompts and sessions it has; and its scope, which is the project name at least 60% of its prompts came from, or Global. The name, summary and scope are marked as data, like the prompts. |
| Benchmarks, naming a pattern | One prompt of the pattern, cut to 1,800 characters. When the cut Prompt Coaching makes leaves any of it, only what it leaves is sent. A prompt that opens with a hook, command or other opening Winner AI knows is sent only as far as a pattern's shown text would show it, at most its first 100 characters: the blocks it opens with, up to but never including a slash command's arguments, text after a closing tag, or a block that is never closed or that holds a shell-mode command, a slash command's arguments or its own opening tag; a command run in Claude Code's shell mode as its bare <bash-input> tag, the command left out, and a prompt that opens with a slash command's arguments as its bare <command-args> tag; an opening that is a line of text rather than a block, such as Claude Code's interruption notice, as those 100 characters, whatever follows it on them up to a shell-mode command or a slash command's arguments; and an opening that cannot be placed, nothing. Any other prompt is sent as stored, a block an agent added included: a prompt an agent composed, such as a review request Claude Code writes for Codex, a Codex app scheduled automation run with the name and saved prompt it carries, or a Codex app writing-block edit with the block text it carries; or a prompt a person typed that the cut's stricter rules drop whole, such as one opening "adversarial review". One that holds a shell-mode command or slash command arguments at the start of a line, in a code fence or out of one, is not sent at all. A pattern whose prompts open with a known hook or plugin template is never named, so never sent. Any other pattern is, whichever kind of session its prompt was issued in, an SDK or automated run included. |
| Benchmarks, replaying a task | The task's prompt, whole, and every system message stored for its session, sidechain ones included, to the endpoint that serves the Route being tried: the model endpoint, or the local one for a local Route. A task's prompt is one a hook or template wrote, so it carries whatever that template put in it, which can be an earlier reply or a diff. |
| Benchmarks, judging a replay | The same prompt, every reply of the original session outside its sidechains, as stored, which includes the model's thinking where the agent records it, and the reply the Route gave. |
The model check, fleetview-model-check |
The labelled set the release ships in its image, judged as Prompt Coaching judges a prompt: each labelled prompt, cut as above, beside its row's agent, session model, position, outcome and counters, ten to a call; then each labelled rewrite beside its prompt, for the rewrite judge. The instructions name a fixed list of frontier families and the cheaper Routes for each, not the team's. Nothing is read from the database, so no team's prompt is sent. |
Text a member typed, and text a model wrote from it (a workflow's name, a
judge's reason), goes to the next call between markers that the instructions
declare to be data. Inside the markers, a run of three or more < or >
is written out as {lt} or {gt}, bracket by bracket, so the text cannot
close a marker or open one. The same goes for the characters drawn as an
angle bracket that Winner AI lists (fullwidth signs, angle quotation marks,
mathematical and CJK brackets) and for a run broken only by spaces or
invisible characters; the list is not every glyph a model might read as a
bracket. Nothing else in the text is changed. This keeps such text in its place in
the request. It does not prove a model will ignore an instruction written
there.
Every call also carries its fixed instructions and the model's name. The replay adds a limit on the answer's length, worked out from the original's token counts; every other call adds the shape its answer must take, and when an answer does not fit that shape the call is made once more with the model's own answer and the validation error added to it. No call adds the session's id, the laptop's name or who typed the prompt, and none reads the sessions' tool-call table, where a tool's result is kept; Benchmarks replays and judges only sessions that used no tool. Only Skills' Generate adds a project name, and only the Benchmarks judge reads a session's replies.
When a call fails, the log line and the error stored on the
pipeline_runs or benchmark_runs row say the HTTP status or the kind of
failure, the model, the name of the shape asked for and how many tries were
made, and why no connection could be made when none was (a refused
certificate, an unknown name). They never hold the request, the endpoint's
response or the model's answer, because an endpoint that refuses a request
can quote it and a
rejected answer is a model's writing about a member's prompts. To see why
a provider refused a call, send it a minimal request with the same model
and response_format using curl. A failed write to the control database
does not print the values it was writing.
Sessions flagged as holding a secret¶
The session collector (agentsview) looks for secrets in every session it
stores and records each one it finds; Home counts them as Credential exposures. A session
with any such finding is flagged, whatever the finding's confidence and
wherever in the session it is. The control plane adds no detection of its own and
does not read what was matched.
While the findings table lists a finding for a session, no call in the table above sends anything from it: not its prompts, its replies, its system messages or the judge's stored results about it. The check is made each time a feature reads, so a session flagged after it was first read is left out for as long as it stays flagged. If the lookup itself fails, nothing is sent: the pass fails, or the page shows no stored result. What it means for results made before the flag:
- Prompt Coaching. Findings already shown stay on the page until a release next changes the rubric version. They are not sent with the main points to improve.
- Skills. A workflow with an example from a flagged session is not listed while it is a candidate or a draft, and cannot be generated or regenerated. An approved skill stays listed and installable. Examples from a flagged session are not shown as evidence.
- Benchmarks. A run whose sample session is flagged is not shown and does not count toward the proven saving. A pattern's name stays until a release next changes the pattern version; a name is never sent to a model.
- The disputes export. A Winner AI developer's tool, run from a checkout of the release and never by the server, writes the prompts a manager disputed on Prompt Coaching into a labelled set in a file it is given, outside the repository, which Winner AI's model evaluation then sends to the model under test; the set a release ships never holds them. It reads as Prompt Coaching does, so a prompt from a flagged session is not written and is listed with the prompts no longer in the organisation's data; a row an earlier export wrote from it is taken out of the file. Until that export is run again, the row stays in the file and the next eval sends it.
What the rule does not do:
- It does not outlast the finding. The findings table is the collector's, and every laptop of the organisation can change it; the collector may also replace a session's findings when the session is pushed again. A session whose findings are removed is not flagged and is sent like any other. That is not caught today.
- It does not catch a secret the collector did not flag. The rule is as good as its detector.
- It recalls nothing. A request made before the flag was already sent, and nothing is deleted from the database.
- It does not cover Insights, which is the Dashboard's own call; Telemetry, below, says what that sends.
Telemetry¶
- Winner AI sends error reports, timings and usage counts only when the
operator sets them. The control plane and the jobs service read seven
operator settings for them (configuration reference,
Telemetry). Error reports leave when
FLEETVIEW_SENTRY_DSNis set to a DSN that passes the host rule; "What an error report carries", below, lists every field. Timings go to the same project while error reports are on, for the share of requests and jobs passes thatFLEETVIEW_SENTRY_TRACES_SAMPLE_RATEsets; "What a timing carries", below, lists every field. While error reports are on and the telemetry secret is valid (set and at least 32 characters), the jobs sweep sends cron check-ins there, unlessFLEETVIEW_JOBS_INTERVAL_SECONDSis under 60, andfleetview-checkinlets a host job (the backup, the certificate renewal) do the same once its script calls it; the backup and renewal scripts this release ships do not call it yet. "What a check-in carries", below, lists every field. Usage counts leave only whenFLEETVIEW_POSTHOG_KEY,FLEETVIEW_POSTHOG_HOSTandFLEETVIEW_TELEMETRY_SECRETare all set and valid; they go from the control plane and the jobs service, best effort, to that host's/batch/. An unreachable analytics host never fails a request or a pass. Settings shows the state of each in its Telemetry group, and "Usage counts, event by event", below, lists every event. What a laptop sends is under "What is never instrumented, and what a laptop does", below. - The browser's usage counts have a path on the deployment's own origin,
/lumen/, and each control-plane page posts its page view, and any uncaught error or unhandled rejection in its own scripts, there. The page loads the analytics library, a pinned build of posthog-js, from the control plane and never from the vendor, and only while usage counts are on: with them off a page carries neither the library nor its settings. While usage counts are on, the web server forwardsPOST /lumen/e/and nothing else, over verified https, toFLEETVIEW_POSTHOG_HOST; a host that names a path keeps it shut, because the edge posts only at the host's root. The vendor never sees the cookie, authorization, identity, client-address,RefererorOriginheaders, and its answer can set no cookie or other header that acts on the whole origin, and is sandboxed. Every other address under/lumen/, page recording's included, answers 404, and so does a request whoseOriginis missing or is not this deployment'sPUBLIC_BASE_URL. The path is behind sign-in like every page, and a removed person is refused. - A hosted model receives what it is sent. "What each feature sends to the model", above, lists it call by call. Insights sends aggregates and a few sample prompts, not transcripts.
What blank settings guarantee¶
FLEETVIEW_SENTRY_DSN, FLEETVIEW_POSTHOG_KEY and FLEETVIEW_POSTHOG_HOST are
blank in a new installation, and blank is off. While they are blank, nothing is
sent, no vendor library is loaded, no telemetry script is rendered into a page
and no connection is opened to an error-report or analytics host. The telemetry
secret alone sends nothing: an installation from the bundle writes one before
any vendor value is set. A blank FLEETVIEW_SENTRY_TRACES_SAMPLE_RATE is not off,
it is 2 in 100, but no timing is sent while the DSN is blank.
An installation may be handed an .env with our values already filled in.
Blanking them and restarting the backend and the jobs service, as below, returns
it to sending nothing: Settings then reads off for Error reporting and Product
analytics.
Switching each kind on and off¶
Each kind is switched by the .env settings below; the configuration
reference gives each its row. Settings shows the
state of each in its Telemetry group, which no one can change from the page.
| Kind | Setting | Sent while | What Settings shows |
|---|---|---|---|
| Error reports | FLEETVIEW_SENTRY_DSN |
the DSN is valid | Error reporting: on with the host and the project number; off while it is blank; invalid. Error report environment: the name every report and check-in carries. While FLEETVIEW_SENTRY_ENVIRONMENT is blank, Settings shows none and reports and check-ins carry production. |
| Timings | FLEETVIEW_SENTRY_TRACES_SAMPLE_RATE |
error reports are on and the share is above 0 | Timing sample rate: the share as a decimal; off while error reports are not on, or when the value is 0 or not a share between 0 and 1. Blank is 2 in 100. |
| Cron check-ins | none of their own | error reports are on, the telemetry secret is valid, and the sweep interval is 60 seconds or more | No row of their own: Error reporting reads on and Identity hashing reads set. |
| Usage counts | FLEETVIEW_POSTHOG_KEY, FLEETVIEW_POSTHOG_HOST and FLEETVIEW_TELEMETRY_SECRET |
all three are set and valid | Product analytics: on; off while the key is blank, whatever else is set; incomplete; invalid. Analytics host: the host name, not set or invalid. |
| Page views and browser errors | none of their own: they ride usage counts | usage counts are on | Product analytics, as above. |
| Page recording | FLEETVIEW_POSTHOG_REPLAY |
never in this release, whatever it says | Page recording: always off. |
| Identity hashing, for every id Winner AI sets | FLEETVIEW_TELEMETRY_SECRET |
not a kind: the key every id Winner AI sets is hashed with, which never leaves the deployment; a page with no one to hash sends the library's own random id instead, as "Usage counts, event by event" says | Identity hashing: set, not set or invalid. |
- Incomplete means the analytics key is set but the host or the secret is not. Nothing is sent.
- Invalid means a value is set but fails its rule: a DSN or analytics host
that is not https or that points at this host's own network, a DSN that names
no key or no numeric project, or a secret shorter than 32 characters. Nothing
of that kind is sent. Where one value is invalid and another is missing,
Settings shows
invalid, because that names what to fix; while the analytics key is blank, Product analytics readsoffwhatever else is set. An invalid secret also stops check-ins and leaves error reports without the hashed deployment id. 0as the sample rate keeps error reports and drops timings.
The backend and the jobs service read these settings when they start. After
editing .env, restart both, then reload Settings:
# in <install>
docker compose up -d backend jobs
What an error report carries¶
Every report is rebuilt from a fixed list of fields before it leaves, so a field this list does not name never reaches the vendor. The vendor also sees the address the deployment connects from, as the receiver of any https request does.
- Every report: an event id, the time, the level, the platform, the
release version, the environment name (
FLEETVIEW_SENTRY_ENVIRONMENT, orproductionwhen that is blank), and the SDK's name, version, package and integration list. - The failure: each exception in its chain by type and module, never its text, with how it was caught (the mechanism's type and whether it was handled), and each stack frame by file, function, module, line and whether it is our code, never a local variable or a line of source.
- A route failure, an uncaught error or a 5xx a route returns on purpose: the method and the route template (never the path as requested), and no header. A failure before a route is matched carries neither, and a 4xx refusal sends no report.
- Tags:
component(backendorjobs);deployment, the hashed deployment id, when the telemetry secret is valid;failure_class(model_callorprovisioning);error_class, the kind of a model failure (transport,http_status,body_not_json,body_shapeorinvalid_output); andpassandfeaturewhen a jobs pass failed. - A laptop's failure report, at most ten an hour per laptop: the message
winner login failedorwinner update failed, the logger namewinner, withcommand,stage,http_statuswhen there was one,error_type,winner_version,agentsview_versionwhen the laptop could read it,os,archanddevice, the hashed Device id, left out when the telemetry secret is unset or too short to hash with, and nothing else. - Never: a request body, a query string, a cookie or a header, an identity or a user, a server name, a local variable, an error or log message, a breadcrumb, prompt text, a repository name, a file path other than a stack frame's, a key, or a setting value other than the environment name above. No sessions or client reports are sent.
What a timing carries¶
A timing says how long a request to one route, or one jobs pass for one team,
took, and how long each database statement, outbound call and model call
inside it took. Each is rebuilt from a fixed list of fields before it leaves,
as an error report is, and goes to the same project.
FLEETVIEW_SENTRY_TRACES_SAMPLE_RATE is the share sampled: blank is 2 in 100,
and 0, or a value that is not a share between 0 and 1, sends none. None is
sent while error reports are off.
- Every timing: an event id, its type (
transaction), its start and end time, the platform, the release version, the environment name, the SDK's name, version, package and integration list, a trace id and span id this deployment made (never one a caller sent), its kind (http.serverfor a request,passfor a jobs pass), its source (routeortask), and the tagscomponent(backendorjobs) anddeployment, the hashed deployment id, when the telemetry secret is valid. No trace header travels with it. - A request: the route template (never the path as requested), the method
when it is one of
GET,HEAD,POST,PUT,PATCH,DELETEorOPTIONS, and the status class (1xxto5xx). A request that matched no route, and the health check, send none. - A jobs pass: the pass name, never the team.
- Inside either, each with its span id, the span id it sits under, the
trace id, its kind (
db,http.clientormodel) and its start and end time: a database statement as its kind alone (SELECT,INSERT,UPDATE,DELETE,WITH,BEGIN,COMMIT,ROLLBACK,SET,CREATE,ALTER,DROP,GRANT,REVOKE,DO,COPY, orother); an outbound call ashttp; a model call as the model's name for this host's model orteamfor a team's own, with its prompt and completion token counts when it succeeded. - Never: statement text or its values, an outbound call's address, path, query or host, a header, a query string, a request body, a prompt, a reply, code, a repository name, a file path, an error message, a local variable, an identity or a raw id. No outbound call carries a trace header, and none sent in is followed.
What a check-in carries¶
A check-in is rebuilt from a fixed list of keys before it leaves, as a report is, so a key this list does not name never reaches the vendor. It is sent only while error reports are on and the telemetry secret is valid: without the hashed deployment id every deployment would share one monitor.
- Type:
check_in, which marks the item as a check-in. - Event id: a random id, which the SDK needs to send it.
- Check-in id: a random id that pairs a run's end with its start.
- Monitor slug: the monitor, its component and the deployment, one of
jobs-sweep-<id>,backend-backup-<id>andbackend-certificate-renewal-<id>, where<id>is the first 16 hex characters of the hashed deployment id. - Status:
in_progress,okorerror. - Duration: seconds, on the end of a run whose start was seen, otherwise none.
- The monitor's schedule: its interval, check-in margin and longest run
time, from which the vendor makes and updates the monitor, and the failure
and recovery thresholds and the time zone,
UTC. - Environment: the environment name, as an error report carries it.
- Never: an error's text, a path, a host or file name, an org, a tag or any free text. The monitor and status come from closed lists and the duration is a number the run measures.
What a browser error carries¶
A control-plane page reports an error its own scripts raise, an uncaught error or an
unhandled rejection, only while usage counts are on and only when what was thrown is an
Error with a stack. The page rebuilds the event from a fixed list before it leaves, so
nothing else the library gathered goes with it. A page sends at most five a load, and none
for an error with no frame in the page's own scripts or whose message holds the page's
address.
- The error's type name where it is one of JavaScript's own (
Error,TypeErrorand the other standard constructors), elseError. - For each frame of the page's own scripts, its line and column, its function where that is a plain name, and the route template as its file.
- The route template as the page (
$current_url,$pathname). - The hashed user as the distinct id, as for a page view.
component, which isweb, anddeployment, the hashed deployment id.- The page view's browser properties and the library's
token.
Never: the message, the address or its query, page text, an attribute value, typed input, a frame of another script (the library's, an extension's, another origin's), or a trail of clicks, inputs or earlier steps.
Usage counts, event by event¶
Every event the control plane or the jobs service sends carries
deployment, this deployment's id hashed under its secret, and
$geoip_disable: true; a page view or a page error, which the browser
sends, carries no $geoip_disable, and only a page error carries
deployment. The lifecycle events below and laptop enrolled also carry
component, the part of Winner AI that sent them: backend or jobs. An
event whose id is not a person's also carries $process_person_profile:
false; a person's key actions are sent under a hashed user, and so is a
page view or a page error where the page has one to hash. Every id Winner
AI sets is an HMAC-SHA256 under FLEETVIEW_TELEMETRY_SECRET, so the
analytics host sees no raw id and cannot reverse one. The one exception is
a page view or a page error on a page with no hashed user: the library then sends its own random id, kept
for the tab, which is no HMAC and names no one; a tab that signs out or
changes person starts a fresh id and never carries the last person's hash.
Never sent: a prompt, a reply, code, a repository name, a file path, a form
field, a name, an email, a setting's value, a model key, an endpoint host, an error message or a raw id. The one
model name that leaves is $ai_model for the operator's own host model; a
team's model is sent as team, because its name may be the customer's. A
Bedrock model id given as an ARN is sent as its last segment, because an ARN
holds the AWS account id.
| Event | When | Id | Properties |
|---|---|---|---|
member added, device revoked, person removed, person restored, org schema migrated, benchmark run queued, team model saved, team model reset, team model key saved, team model key removed, licence uploaded, skill generated, skill regenerated, skill candidate dismissed, skill edited, skill approved, coaching finding disputed, laptop login decided, repo choice queued |
A person's change, after its route answers below 400 | The person, hashed | route (the route template, never the address), status_class (2xx or 3xx), org (hashed), deployment |
setting saved, setting reset |
A person's change, after its route answers below 400 | The person, hashed | route (the route template, never the address), status_class (2xx or 3xx), org (hashed), setting_key (the setting's key on Settings, never its value), deployment |
pass completed |
Once per pass run | The team, hashed | feature, pass_version, outcome (succeeded, failed or skipped), rows_in, rows_out, duration_ms, model_source, deployment |
$ai_generation |
Each model call, from a pass or from Skills' Generate and Regenerate, a failed call included | The team, hashed, inside a pass or a Skills generation; this deployment, hashed, otherwise | $ai_trace_id, $ai_model, $ai_provider (openai-compatible, or amazon-bedrock for a call through Amazon Bedrock), $ai_input_tokens, $ai_output_tokens, $ai_latency, attempts, finish_reason (stop, length, tool_calls, content_filter or function_call; any other value the endpoint gives is sent as other), $ai_is_error, error_class (the class name, never the message), feature, pass_version, model_source, deployment |
winner login failed, winner update failed |
When an enrolled laptop reports that the command failed, at most ten an hour per laptop | The laptop, hashed | stage, http_status, error_type, winner_version, agentsview_version, os, arch, deployment |
laptop enrolled |
Each successful enrolment, a re-login included: a re-login enrols a new laptop row under a new hashed id, so these count enrolments, not distinct laptops | The laptop, hashed | org (hashed), os, arch, winner_version (each a release target or a bare version; any other value is sent as other), component, deployment |
deployment installed, telemetry switched on |
The first start with usage counts on; a start with them on after one with them off | This deployment, hashed | component, deployment |
deployment heartbeat |
Once per UTC day, from the jobs service | This deployment, hashed | version (this release, or other), seats (people with at least one enrolled laptop), laptops_pushing, laptops_stale, laptops_quiet, laptops_never, winner_version_counts, os_counts, arch_counts (counts of enrolled laptops per value), component, deployment |
deployment upgraded |
When scripts/upgrade-server.sh completes an upgrade, from the new release's control plane |
This deployment, hashed | version (the new release, or other), component, deployment |
deployment rolled back |
Not sent yet | This deployment, hashed | version, component, deployment |
licence state changed |
From the jobs service, when its sweep finds the licence state moved; the first sweep after the upgrade records the state without sending | This deployment, hashed | licence_state (licensed, grace or lapsed), component, deployment |
$pageview |
Each load of a control-plane page while usage counts are on, sent by the browser through /lumen/; a form whose answer is a whole page (a refused form, Add row and Remove row, the new member's one-time page) counts as a page view of the form's own route; never the documentation, the laptop-login approval page or the dashboard |
The person, hashed; where the page has no one to hash (a person with no roster row where Auth0 holds the roles, or the host-run bypass before its roster row exists), the library's own random id for the tab, which names no one | $current_url and $pathname (both the route template the server matched, never the address or its query), $lib, $lib_version, $browser, $browser_version, $os, $device_type, $session_id, $window_id, $process_person_profile, and the library's own token (the analytics project key, public by the vendor's design) and distinct_id (the hashed id, or that random id), without which the event is not sent or is filed under no one |
$exception |
An uncaught error or unhandled rejection in a control-plane page's own script while usage counts are on, at most five per page load | The person, hashed, as for a page view | $current_url and $pathname (both the route template, never the address), $exception_list (the error's type name and, for each frame of the page's own scripts, its line, column, function where that is a plain name, and the route template as its file; never the message), component (web), deployment, $lib, $lib_version, $browser, $browser_version, $os, $device_type, $session_id, $window_id, $process_person_profile, and the library's own token and distinct_id, as for a page view |
The deployment heartbeat is not the operator's dead-man's switch
(FLEETVIEW_HEARTBEAT_URL), which is unchanged and sends nothing to Winner AI.
A Benchmarks replay call to a Route is counted as team, in $ai_model and
model_source alike, because its model is named by the Route.
A refused form sends no key action, and neither do the Benchmarks routes
editor's Add row and Remove row, which only redraw the page. Each redrawn
page still sends its page view, under the form's route: /settings/{key}
for the two buttons.
What is never instrumented, and what a laptop does¶
- The Dashboard is never instrumented. Its pages carry no analytics library, no error reporter and no page view. The only script Winner AI adds to them is the brand script, which sends nothing.
- The Dashboard's telemetry is switched off. The pg serve container
runs with
AGENTSVIEW_TELEMETRY_ENABLEDset to0and--no-update-check, and the control plane's schema setup runs withAGENTSVIEW_TELEMETRY_ENABLEDset to0. - The collector's telemetry on laptops is switched off too. Every service
winnerinstalls setsAGENTSVIEW_TELEMETRY_ENABLEDto0andAGENTSVIEW_DISABLE_UPDATE_CHECKto1, and the collector's config, which enrollment writes, setsdisable_update_check = true. A daemon you start yourself afterwards, from the agentsview desktop app or a shell, carries your own environment instead, so those two variables do not reach it. What the laptop sends, and where says what a laptop still reaches, which includes the collector's model-price refresh, which has no switch. - A laptop contacts no telemetry vendor.
winnerholds no vendor address, key or telemetry state. It reports a failedwinner loginorwinner updateto the control plane it enrolled against and nowhere else. The one exception to this bullet is the daemon you start yourself, above. What else a laptop reaches, none of it telemetry, is in What the laptop sends, and where. - The report is not stored. While error reports or usage counts are on, the
control plane builds the events listed above from it (the laptop's error
report, and
winner login failedorwinner update failed) and keeps no copy. It holds only the time of each report, in memory, to keep a laptop to ten an hour, whether or not telemetry is on. With both off it builds nothing, and the laptop is answered the same either way. - Page recording cannot be switched on in this release. It is built and
held off whatever
FLEETVIEW_POSTHOG_REPLAYsays, and its Settings row readsoff.
There is no developer switch. A developer cannot turn the report off on the
laptop; the operator's settings above decide whether the control plane passes
anything on. winner login prints this notice once the laptop is enrolled,
and When winner fails shows it too:
If
winnerfails after this laptop is enrolled, it sends a short report to your organisation's Winner AI server: the command, the step, the HTTP status, the kind of error, the versions, and your operating system and processor type. The report never contains prompts, code, repository names, file paths, emails or the text of an error. If your organisation's operator has switched telemetry on, the server passes those fields on to Winner AI's error reports and, under a scrambled laptop id, its usage counts.winnersends the report nowhere else.
Where it goes¶
Each kind goes where its setting points. The values Winner AI hands over point at Winner AI's own projects: error reports, timings and check-ins at Sentry, and usage counts, page views and browser errors at PostHog. Winner AI holds no copy of a deployment's telemetry secret, so it cannot reverse a hashed id.
Some of what happens to an event happens in those projects, where a deployment cannot see it. We keep them set as follows:
- Sentry: its data scrubber is required, with its default rules; IP addresses are not stored; events are kept for 30 days. Sentry still derives a country and city from the address an event arrives from before it drops the address, so an error report can be kept with a location near the server that sent it.
- PostHog: the client address is discarded and its GeoIP transformation is off, so no location is kept; events are kept for 30 days; session recording, autocapture, heatmaps, exception autocapture, web vitals and console-log recording are off.
- Keys: every deployment we hand values to sends usage counts under one
shared analytics key, and deployments are told apart there only by the hashed
deployment. Each gets an error-report key of its own, which we can revoke alone.
If you later blank FLEETVIEW_SENTRY_DSN or the telemetry secret, or rotate the
secret, which changes the hashed deployment id, the check-in monitors we hold
for your deployment stop hearing from it and report it missed. That alert stands
with us until we remove the monitor, so we may ask whether you meant it. Nothing
on your side needs doing.