Repository navigation
efr 0.0.3
Added
- Model calls to OpenAI can go over a WebSocket, which cuts the time to the first
token. Each conversation keeps one connection open between the calls of a turn and
between turns, and closes it after 10 minutes without a call. Before a call on a
connection that had no call for 30 seconds, efr sends a ping. When the connection
died during a sleep of the computer or a change of network, the call goes over HTTP
after at most 5 seconds. While the connection holds the previous answer, a call
sends only its new input. A call that the server did not take on the WebSocket goes
over HTTP. An error answer on the WebSocket, such as a rate limit or a context
overflow, fails the call as it does over HTTP, without a second request. A call
whose request went out and whose connection then broke or went silent fails as on
HTTP and does not go out again, because the model may already run. When you
interrupt a turn, efr stops the answer on the connection and keeps the connection
for the next call. The new key[openai] websocketchooses the transport:auto
(the default) uses a WebSocket for each model that the model catalog marks with
prefer_websocketsand HTTP for the other models, so a new catalog from the backend
changes the choice from the next call.onuses it for every model, andoffuses
HTTP only. The key needs a restart of efrd. The debug log shows the time to the
first event of each call with its transport (phase=provider_acceptedand
phase=provider_first_event), so you can compare the two. - The model edits files with a new
apply_patchtool. One patch adds, changes,
deletes or moves one or more files, and it changes all of them or none: when a part
does not match, the model gets the nearest lines of the file and no file changes.
When you interrupt the turn while the patch writes, efr puts every file back.
The call shows the diff of each file. A change or a new file in the turn's project
or in$SCRATCHneeds no approval, as forwrite_file. A delete or a move always
asks, also inauto, because undo cannot bring the file back yet. The question
shows the diff of every file and marks each delete and move. The models that take
freeform tools get the tool with its grammar. When you change to a model that does
not take freeform tools, the earlier calls go to it in the form that it takes. The
system prompt tells the model to use it, and notsed -ior a whole-file write,
for an edit. efrshows anapply_patchcall by its files, such asapply_patch src/a.rs +3 −1, new notes.md +2, delete old.rs, and never the text of the patch. After the call,
each file gets its own diff block with a row that names it, and
render.diff_linesapplies to each file. The approval of a patch shows the diff of
every file, and marks a delete or a move in the warning colour. A file outside the
project,$SCRATCHand your home keeps its absolute path, such as/etc/hosts.
While the model writes the patch, the status row showspreparing apply_patch, 3.2 KB.- An input row below a running turn, in the terminal that follows it. Type there while
the turn runs: Enter steers the turn, Tab queues the text as a prompt behind it, Esc
interrupts it and Alt+Up takes back the newest prompt that you queued there. The row
edits like a shell line (arrows, Home, End, Ctrl+A/E/U/W/K, Alt+B/F), Ctrl+J adds a
newline, and a paste keeps its newlines. Keys that you type while the prompt goes
out land in the row too. An approval takes only the keys that you type after it
shows: keys typed before it and the rest of a paste stay in the row. While the
output of a command ends with a password prompt, the row hides, and what you type
never goes to the model as a steer. A complete output line that only names a
password, asgrep passwordprints, does not hide the row.
render.turn_input = falseturns the row off. - Steers that the model did not read yet show above the status row as
↳ steer:, and
prompts that you queued as↳ queued:. When a model call reads a steer, it moves
into the reply as your message. A steer that comes too late for its turn waits in
the queue instead. efrfollows each prompt that you queue from the input row after the turn before
it, and ends after the last one. Esc sends the steers that the model did not read as
a new prompt, and puts the prompts that you queued back into the row, so nothing
runs that you did not see. Esc stops the turn also when the queue is full or when
the settings of your terminal do not work now: the steers then go with the settings
of the stopped turn. Ctrl+C clears the row; on an empty row it interrupts the
turn and also takes back the prompts that you queued there and the steers that the
model did not read. A prompt that still waits behind another turn is taken back
instead, by Esc and by Ctrl+C. When efrd does not answer Ctrl+C in time, or the
connection ends, all of that text goes back to your shell with a note, so no text
is lost.- Text that is still in the input row when
efrends goes back to your zsh command
line as, <text>, so you can edit it and send it again. Without the zsh plugin,
efrshows it as one muted line. Eachefrrun gets a file of its own, so the
texts of two runs before the next prompt both come back, also from a pipeline or
after you source the plugin again. A new shell removes only the files of gone
shells in its own pid namespace, so a shell in a container (distrobox, toolbox)
keeps its text. - When efrd restarts while
efrfollows a turn with the input row,efrconnects
again. Prompts that you queued and steers that the model did not read come back to
your command line when the restart cancelled them. A steer or a prompt whose answer
the restart lost is not sent twice. When efrd does not come back, they come back
too. SIGTERM and SIGHUP endefrat once also while it connects again. - The diff of each file write shows in its call block, in the diff colours: the first
render.diff_lineslines (default 20), then a muted… N more lines. This works in
every directory. - A shell call that changed files gets one muted row under its result, such as
changed src/a.rs +3 −1 · deleted old.rs · new notes.md (+2 more). A turn that
changed files gets a muted line such as3 files changed, +24 −7before its end.
This works in registered projects (git or not) and in$SCRATCH. The row and the
line show what changed while the call or the turn ran, also a file that another
conversation or your editor saved at the same time. efr diffprints what a turn changed: by default the last turn of this terminal's
conversation,--turn <id>for another,--statfor the list of files. In a pipe it
writes the plain diff. An ignored file such as.envshows in the list, but its
content stays out of the diff.- efr's own snapshot store: one bare git repository per registered project or
$SCRATCHbelow the data directory. It never writes the project's.gitor index.
The[snapshot]table sets it (snapshot.enabled, size limits,keep_turns,
max_age_days). - efrd has the parts that the input row of a turn needs. A steer that comes too late
for the running turn can become a queued prompt (turn.steerwithif_late). You
can take back a prompt that waits in the queue before it starts (prompt.withdraw).
An interrupt can take back the queued prompts of one terminal and send its unread
steers again as one new prompt, which runs next with the settings of that terminal.
An interrupt can also take back unread steers, so that no model call reads them
(steering_withdrawn). efrd records when a model call reads a steer
(steering_delivered).efr historyshows a prompt that you took back as
withdrawn before it ran, and steers that you took back astook back a steer that the model did not read. - efrd sends the conversation id as the prompt cache key of every model request, and
on the subscription also as thesession-idheader, as Codex does. The requests of
one conversation then go to the same prompt cache. turn_completed,turn_failedandturn_interruptedsay how full the model's
context is (context: the tokens, the point where efrd compacts and the window).
Theirusagealso gives the cached input tokens, the reasoning tokens and the
context of the last model call (context_tokens). A client that follows a turn gets
acontextdraft before and after each model call.- An entry of
[openai] modelscan be a table that gives the model's limits:
{ id = "gpt-next", context_window = 400000, max_output_tokens = 128000 }. An
entry can also name a built-in model to change its limits.models.listshows the
context window of each model that efr knows. On an API key, a model's own output
limit wins over[model] max_output_tokens. - efr compacts the context of a conversation before it fills the model's window. When
a request reaches[compaction] auto_atpercent of the window (76 by default),
efrd first gives old tool outputs a short stub, when that frees at least 20000
tokens. When that is not enough, the conversation's own model writes a summary with
fixed sections: the task and its state, the decisions, the important details, the
files and places, the open work and the next step, the directories and the shell,
and the open system tasks. The next requests send a fresh block that efrd reads from
disk (your directory, the hidden shell's directory and its jobs, the git status and
theAGENTS.mdfiles from the project root down), the summary, and the newest
messages word for word, about 20000 tokens. The turn goes on after the compaction.
[compaction] auto = falseturns it off. All events stay in the log. The history
keeps every earlier turn that the model's window can hold, so the compaction makes
room, not a limit of turns or bytes. A summary that the model cuts off at its output
limit never replaces the history. When the history does not fit in the summary
request, its oldest messages go, and the summary, the compaction's line and the log
say how many. efrshows how full the model's context is. The status row of a running turn, and
the line of a running command, end withctx 43%, and the end-of-turn line reads
done in 42s, ctx 43% (89k/206k), 1.1k out. 100% is the point where efr compacts
the context. The field is green below 50%, yellow from 50% and red from 90%, in the
colours of your theme. While efr compacts the context, the status row says
compacting context. Each compaction leaves one dim line, such ascontext compacted (auto): 231k -> 24k tokens, kept 3 turns, summary 3.2k, or a line that
says that the context is full and what to do.efr historyshows the same line at
the place of each compaction, and--verboseadds the summary. A client that
attaches while a turn runs shows the field at once.efr compact [focus]and,compact [focus]compact the context of a conversation
now, between turns: efr writes a summary of the earlier turns and keeps the newest
turns word for word. The focus says what the summary must keep. It starts no turn,
and a prompt that you send meanwhile waits for it. While a turn runs, efr refuses
it. A retry of the same command waits for the compaction that runs. The plugin
hands the focus toefrin its environment, as it does a prompt.- efr knows the current OpenAI models (gpt-6.1-sol first) with their windows and
efforts, from a table built into efr. efrd also asks the ChatGPT backend for its
model list in the background, at start, afterefr login openaiand then every
hour, with efr's own version. Today the backend lists its models only for Codex
versions, so the built-in table stays in use. A prompt never waits for this
request. When the backend sends a list that efr can use, it applies from the next
turn on, and efrd keeps it in its state directory
(~/.local/state/efr/model_catalog.jsonby default) for a start without network.
Models that the backend hides, or whose tools efr cannot send, are not offered. efr modelsshows the context window of each model and the largest window that
[openai] modelscan set, such asgpt-6.1-sol 272k (up to 872k), and a last line
that says where the list came from:models: from the backend, fetched 5m ago,
from the cache, fetched 2h 5m agoorbuilt into efr.efr statusshows the same
as itsmodelsline.- A
context_windowin[openai] modelscan raise or lower the window of a model of
the list, up to its largest window. Above that, efrd uses the largest window and
warns once in its log, andefr config checkadds a note. - The token counts of a turn on the protocol now include the input tokens that the
provider wrote to its prompt cache:cache_write_tokens, and
cache_write_1h_tokensfor the part that it keeps for an hour. efr reads OpenAI's
count of cache writes. Likecached_input_tokens, both are parts of
input_tokens. - The end-of-turn line shows the part of the turn's input that the provider's prompt
cache served, such asdone in 42s, ctx 43% (89k/207k), 1.1k out, cache 91%. A
cache write counts as a miss. The line shows it only when the turn's input is 2048
tokens or more, because a smaller input never makes a cache entry. - Claude models with an Anthropic API key, provider
anthropic-api. Log in with
efr login anthropic, set[model] provider = "anthropic-api"and restart efrd.
efrd streams Anthropic's Messages API, sends the signed thinking of each answer back
unchanged, and sends an effort to each model that takes one:mediumunless you
set another. A new[anthropic]table hasbase_url,models,cache_ttland
workspace_id(for a key that is not scoped to one workspace).cache_ttl = "auto", the default, keeps
everything in the prompt cache for an hour, because an entry for one hour cannot
build on an entry for five minutes;5mkeeps everything for five minutes. - The debug log of efrd gives each model call the time since the start of the
conversation's call before, asgap_ms. Each request to Claude also writes one line
with its prompt cache markers and their times, such ascache_ttl=auto markers=S1h,A1h,P1h,T1h. With the token counts of the turns, these lines show which
[anthropic] cache_ttlcosts less. - efr has no built-in list of Claude models. The models, their windows, their output
limits and their efforts come from the API's model list, which efrd fetches at
start, after a login and every hour, and keeps inanthropic_model_catalog.jsonin
its state directory. Until efrd has a list, a prompt waits for one fetch. The
default model isclaude-opus-5-5. Without a key, a turn fails and says to log in. efr modelsandefr statusname the provider of the model list, such as
models: anthropic-api, from the backend, fetched 5m ago, and say when efrd has no
list yet.efr config checkalso notes a window of[anthropic] modelsabove the
largest window of its model.- A new
edittool for Claude models. It replaces the exact textold_stringof one
file withnew_string, once, or at every match withreplace_all. When the text
occurs more than once withoutreplace_all, or does not occur, no file changes and
the model gets the count or the nearest lines of the file. In a file with Windows
line ends, the model's line breaks match them. A request offers a model either
apply_patch(OpenAI models) oredit(Claude models), never both, and a call of
the other one does not run. An edit needs approval where awrite_fileof the same
file does. The question shows the diff of the file, andefrshows the call as
edit <path>. - Logins with an API key:
efr login openai-apiandefr login anthropic(also
anthropic-api). efr reads the key at a prompt that does not show it when stdin is
a terminal, else from stdin, or with--from-envfromOPENAI_API_KEYor
ANTHROPIC_API_KEYof your shell. It never takes the key as an argument. efrd
checks the key with one request that runs no model,--no-checkskips the check,
and then efrd keeps the key in itssecrets/directory. A login does not change
the provider of new conversations: efr says how to set[model] providerand that
efrd needs a restart. efr logout <provider>forgets the login ofopenai,openai-apioranthropic.
A key stays valid at its provider; efr says where to revoke it.efr statuslists every provider (openai-subscription,openai-apiand
anthropic-api), shows the start and the last four characters of each stored key,
such assk-ant-...a1b2, and marks the provider of new conversations with
(active). Its line about a missing login names the login of that provider.efr logoutof the provider of new conversations warns on stderr: the provider
stays in[model] provider, so its turns fail until you log in again. A logout
recordslogout_completedin the event log, with the provider and nothing of the
key. The answer ofadmin.logouthas a newactivemember.efr statusshows when a provider refused its stored API key, such askey sk-ant-...a1b2 refused at 2026-10-09T12:03:00Z (5m 0s ago), after a 401 on a model
call or on the fetch of the model list. The mark goes away when a request of the
same kind with the key works again, or after a new login or a logout. After a model
call that worked, a refused fetch of the model list does not show, so a key whose
scopes allow one endpoint and not the other does not flip the mark each hour. For
the active provider, a line
on stderr says that its turns fail and how to log in. A provider in the answer of
admin.statushas a newkey_refused_atmember.- After
efr login openai-apiand afterefr config set model.provider openai-api,
one line says that OpenAI bills the API key per token, apart from a ChatGPT plan. - A
[model] nameof another company than[model] provider, such asgpt-5.5with
anthropic-api:efr config checkwarns about it with the fix, efrd logs a
warning at start and after each reload, and a prompt that names no model of its own
fails at once with the cause and the fix (set[model] nameto a model of the
provider, or remove it). An id in the config's list of models of the provider's
company is not of another company. - When efrd has no list of Claude models at the start of a turn, the prompt now fails
with the cause instead of a turn that says that the provider does not serve the
model. Without a stored key, it sayslog in with efr login anthropic. After a
failed fetch, it saysefr could not fetch Claude's model listand why, such as the
API's message for a refused key. A model whose output limit the config gives still
runs without a list. - New keys
[openai] organizationand[openai] project, sent as the
OpenAI-OrganizationandOpenAI-Projectheaders of every request with an API
key. Both need a restart of efrd. - A new page,
docs/providers.md, tells how to log in with an API key, how to change
the provider, which settings the Claude models have ([anthropic] cache_ttland what
autodoes, the workspace id, a smaller window), and what makes the prompt cache
miss. - Do not put an API key in the environment of the efrd service: efrd does not read
it, and it gives its environment to the hidden shells, where a command of the agent
can read it outside theautosandbox.docs/providers.mdsays more.
Changed
-
Each request to the model now starts with the request before it and its answer, so
the provider's prompt cache reads the earlier turns again and a long conversation
costs less. The prompts of earlier turns keep their<live_state>block, and the
system prompt tells the model that only the newest block is current. efr keeps the
exact messages of every turn since the last summary, not of the newest 50 turns
only, and a conversation of more than 4096 events no longer leaves out its oldest
turns. Before, each turn changed the prompt of the turn before it, and from the 51st
turn on each turn also changed the oldest turn. -
When the history would leave out a turn, such as after a change to a model with a
smaller window, the turn compacts first, also below[compaction] auto_at. Before,
the history left out one more turn at each turn. -
The fresh context block of a compaction (your directories, the git status and the
AGENTS.mdfiles) is now part of its record, andconversation_compactedcarries
it asfresh. After a restart of efrd, a request sends the same block. Before, the
next turn read the files again. -
The default system prompt tells the model to change files with the edit tool that
it has,apply_patchoredit, not withapply_patchonly. -
With an OpenAI API key, efrd asks the API for the ids of the models that the key
can use, at start, after a login and every hour, and offers only those models of
its built-in list. The efforts are the API's: no model offersultra, and
mediumis the default effort of every model. -
An OpenAI answer of 429 for a used-up quota or a spend limit, such as
insufficient_quota, now fails the turn at once. Before, efr sent the same request
up to three more times. -
With an OpenAI API key (
openai-api), a key that the server refuses now fails the
turn at once, and the error shows the server's reason, such as an expired key or a
missing scope. Before, efr sent the same key a second time and showed no reason. -
The default model is now the first model of the backend's list (gpt-6.1-sol today),
not gpt-5.5, which the backend lists as its legacy model.[model] namestill wins.
The first id of[openai] modelsno longer picks the default model. The window,
the efforts, the default effort and the tool form of each model come from the
backend's list too. With an API key, the built-in list stands in, so a model that
it does not hold needs an entry in[openai] models. -
When efrd cannot fetch the model list, it tries again after 15 seconds, then after
30 seconds, 1 minute and 2 minutes, then every 5 minutes. A good fetch starts the
waits over. Before, it waited 5 minutes each time, so after a start before the
network was up, the list came late. -
A long command no longer leaves a short first word alone in its row (
cp \). A row
breaks at a space only when it is at least half full; else it breaks inside the word
with↩. -
The hint of an empty input row starts one column after the cursor. Before, a block
cursor stood on the first letter of the hint and seemed to cover it. efr does not
change the cursor shape. -
The note
interrupted to send your messagenow comes after the call that Esc
stopped and after the end of the interrupted turn. Before, it came above the
stopped call. -
efr historyshows the lines of each turn together, and the turns in the order
that they started. Before, it showed the events in the order that they came, so a
prompt that you queued or sent again with Esc showed in the middle of the turn
before it. A prompt that you took back shows where it was in the queue, with the
notewithdrawn before it ran. -
A call in
autocosts less in a project with nested git repositories. The surface
guard runs git only for a git config that changed since the last call, reads no
*.samplehook, and lists directories faster. In a copy of efr's own repository, the
guard went from 15 ms to 6 ms per call. -
When the model refuses a request as too large for its context window, efrd never
sends that request again as a retry. It compacts the context once and sends the
request again. Before each model call, efrd estimates the size of the request, and
it never sends a request above 95% of the window. When the model refuses the request
again, when auto compaction is off, or when the request stays above the cap, the turn
fails with a message that names the cause, the size and the way out, such asthe context is still full after a compaction: the model refused about 281k of 272k tokens; run ,compact or start a new conversation. When the compaction fails, the
message also says why, such asthe compaction failed (the provider is rate limiting requests). After two compactions in a row that leave the context too full, efr
stops compacting on its own and says so, also in the next turns, until the context
is below the trigger again or you run,compact. Before, the turn failed with the
provider's own message. -
When the history of a long conversation leaves out earlier turns, the model reads
3 earlier turns are omitted.first, and efrd logs a warning. Before, the turns
went without a trace.
Fixed
- efrd no longer loses a steer at the end of a turn. Before, efrd accepted a steer
that came after the last model call of the turn or after an interrupt request, and
no model call read it. Now efrd never records such a steer. It refuses a steer from
,!with a conflict, so you can send the text as a new prompt. A steer from the
input row becomes a queued prompt. - efrd now ends a turn before it tells the clients that the turn ended. Before, a
steer or an interrupt that you sent just after you saw the end could go to the
ended turn, and a prompt could wait behind it. Now efrd refuses the steer and the
interrupt, and the prompt starts at once. - Ctrl+C while the auto sandbox starts a call now stops the call, with
status 130. Before, the call could fail with status 125 and the message "the sandbox
could not start", and efrd checked the sandbox again for no reason. A Ctrl+C that
came just before bwrap started was lost, and the command ran to its end. - The sandbox launcher no longer hangs when Ctrl+C ends bwrap while bwrap starts the
sandbox. - Ctrl+\ (SIGQUIT) now stops a shell call of the auto mode, as Ctrl+C does. Before,
the child shell of the call ignored it. A Ctrl+\ during the line stopped only the
running command, and the rest of the line ran. A Ctrl+\ just before the line was
lost, and the line ran. - A
TRAPINTorTRAPQUITfunction no longer lets a line of the auto mode go on after
Ctrl+C or Ctrl+. Before, such a function of your shell, or one that an earlier call
made, could catch the signal in the child shell of the call. - A client that resumes
pty.attachwithsince_seqnow gets each new size that the
PTY took while the client was away, asresizedat its place in the output. Before,
the recording did not keep sizes, so the client got the output only. The recording
keeps the sizes now, and older recordings still read. - The time that a new hidden shell takes to show its first prompt no longer counts
against thetimeout_secondsof a shell call. A slow.zshrcmade short calls fail
with "an earlier command is still running", although no command ran. The startup
timeout (10 s) limits that wait now. When a new shell never shows a ready prompt,
the model reads that no command ran in it yet. - The hidden shell now follows you when you change directories between two prompts.
Before, it stayed where the model left it, so the model worked in the old directory
or used long paths. Now efrd moves the shell to your new directory before the turn
starts. The shell keeps its variables. When you did not move, the shell stays where
the model put it. While a command still runs in the shell, the shell stays, and the
model reads where it is. - The model now reads that you changed directories since the previous prompt, and
whether its hidden shell moved with you. Before, a short follow-up such asand here?could go to the old directory. - After a restart, the model reads each steer of an earlier turn at the place where
it read the steer during that turn. Before, a turn without saved messages showed a
steer where you typed it, and also showed steers that the model never read, such as
a steer of a failed turn or a steer that you took back. - A conversation that grew on a model with a large window now goes on with a model of
a smaller window, such as a move from a Claude model (1M tokens) to an OpenAI model
(272k tokens). The first turn on the new model compacts before its first call, and
the summary request fits the new window: it first leaves out the newest messages,
which stay after the summary word for word, and then the oldest messages. The
summary says when it left out old messages. Before, efr sent the whole history in the
summary request, and the provider refused it once as too large. - When you change to a model with another edit tool, such as from a Claude model with
editto an OpenAI model withapply_patch, the new model reads the earlier calls of
the other tool, and their results, as text. A call of a tool that efr no longer has
shows as text too. Before, efr sent them as calls of a tool that the request did not
offer, which a provider can refuse. The text is the same in every request, so the
prompt cache still reads it. When you change back, the calls go in their own form
again. - After a switch of
[model] provider, the first turn on the new model estimates its
request. Before, it started from the context that the old model reported last, and
when that count was above the cap of the new window, the turn failed as full. ,compactright after a switch of[model] providernow uses the default model of
the new provider. Before, it sent the summary request with the model of the newest
turn, which the new provider does not serve, and the compaction failed.- When the newest messages alone do not fit in the context, the error now says to
start a new conversation. Before, it said to run,compact, which then answered
that there was nothing to compact. - On Claude, when the model calls a tool that the request does not offer next to a
tool that it offers, in one answer, the next request of the turn keeps the thinking
of that answer. Before, efr sent that answer without its thinking, and the Messages
API can refuse such a request, so the turn failed. - On Claude, a prompt after a turn that ended early now goes as a message of its own,
as after an interrupt while a tool ran or after a model call that failed, and the
new turn now gets its one-hour cache entry. Before, efr joined the prompt to the
last message of that turn, and the new turn had no one-hour cache entry. - Each
<live_state>block now starts with the rule that only the newest block is
current. Before, only the default system prompt said so, and a[model] system_promptof your own lost the rule, so the model could read an old directory
as the current one. Blocks that efr sent before keep their bytes. - The first key that you type after a question or a password prompt appears no longer
goes to the input row. Before, when efr read a key from the terminal at the moment
the question appeared, efr could count that key twice, and then the next key, such
as the first letter of a password, went to the row.
Security
- The
<live_state>block shows your last command with its secrets redacted: the
value of an assignment to a name that looks like a secret, such as
export GITHUB_TOKEN=...or--api-key=..., also inside the value of an option,
such as--env=API_TOKEN=...or--from-literal=password=...(a count such as
--max-tokens=4096orTOKEN_COUNT=5stays); each word in the form of a known key
(sk-ant-,sk-proj-,sk-,ghp_and the other GitHub token forms); the password
of a URL, such ashttps://user:...@host; the value of anAuthorization,
Cookieor other secret header, such as-H 'Authorization: Bearer ...', without
its scheme, where a header stands (a search pattern such as
grep "Authorization: Bearer"stays); and the password option of the programs that
take one, such asmysql -p...,sshpass -p ...,docker login -p ...and
curl -u user:..., also among other short options in one word
(curl -su user:...). efr now saves the block with the turn and sends it again with
every later request, so a secret there would reach the disk and every request after
it. The list of patterns cannot find every secret: a secret in another form stays in
the command.