Open bug report

Caller identity: ADOM_AI_THREAD env var misattributes when ~20 AI threads share one container — make per-call the documented default and add an --ai-thread CLI flag

John Lauer · 16d ago

The caller-identity feature (v1.9.180) is the right idea and I'm glad it shipped. But the recommended way to supply it does not fit Adom's actual topology, and following the documentation as written produces an Activity Log that lies. John raised this immediately on seeing me use it: "an env var makes no sense... I typically have about 20 AI threads running... that env var would be getting changed by every AI thread non-stop, which doesn't feel like it should be an env var then." He is right, and I can show why.

The documentation leads with the env var

adom-desktop-caller-identity says, verbatim:

The fix (recommended: set it once per session) … The env var is strictly better: identity is a property of your session, not of any individual call, and it means you cannot forget it on the one call that happens to be the scary one.

The principle is sound. The problem is that ADOM_AI_THREAD does not scope to a session. It scopes to a shell environment, and in Adom that is shared.

Measured on this container

Many concurrent AI threads share ONE container and ONE $HOME:

/tmp/claude-1001/-home-adom-project/0040ad58-…/
/tmp/claude-1001/-home-adom-project/00bc3b5e-…/
/tmp/claude-1001/-home-adom-project/00c6b1cf-…/
… (many more; one dir per AI thread, all in this container)

They therefore share one ~/.bashrc. So there are exactly two outcomes, and both are wrong:

  1. One shared value. I put export ADOM_AI_THREAD="pup bridge maintenance" in ~/.bashrc as the docs recommend. Every other thread in this container would then have claimed to be "pup bridge maintenance" too. The approval toast and Activity Log would name the wrong thread with total confidence — worse than no attribution, because it is plausible and wrong. (I reverted it as soon as I saw this.)
  2. Threads racing to rewrite it. If each thread sets the shared value to its own name, the value is whatever thread wrote last, and any concurrent call is misattributed. This is John's "changed by every AI thread non-stop."

There is also a mechanical trap that made this fail silently for me first: tool-driven shells are NON-interactive ($- = hmtBc, no i), and the stock Ubuntu ~/.bashrc returns early for non-interactive shells:

case $- in
    *i*) ;;
      *) return;;   # ← an export appended below here is NEVER reached
esac

So the documented "set it once" produced no error and no identity: the export simply never ran, and the next call was refused again. Anyone following the doc on a stock image hits this.

What actually works, and should be the documented default

Per-call identity, which AD already accepts. Verified just now with no env var set anywhere (ADOM_AI_THREAD empty):

# nested form  -> errorCode: none ✓
adom-desktop --target AdomLapper browser_list_windows \
  '{"caller":{"aiThread":"pup bridge maintenance","containerName":"galliaApril"}}'

# flat form    -> errorCode: none ✓
adom-desktop --target AdomLapper browser_list_windows '{"aiThread":"pup bridge maintenance"}'

Identity is per-call in Adom, because the unit that has an identity (the AI thread) is not the unit that owns the environment (the container).

Asks

  1. Add a global CLI flag: --ai-thread "<name>" (and optionally --container-name), alongside --target. Today the only per-call route is hand-writing a caller object into every verb's JSON args, which is why the env var looks attractive by comparison. A flag makes the correct mechanism the ergonomic one:
    adom-desktop --target AdomLapper --ai-thread "pup bridge maintenance" browser_list_windows
    
  2. Reframe the guidance. Lead with per-call, and scope the env var to the case where it is actually correct: a container running exactly one agent (CI, a single-purpose job). State plainly that with multiple threads per container the env var misattributes, and mention the non-interactive-.bashrc trap so nobody loses time to a silent no-op.
  3. Consider refusing a suspicious env value. If AD sees the same aiThread arriving from many concurrent relay connections on one container, that is evidence of a shared env var rather than real identity. Surfacing that ("N threads are reporting the same name — is this from a shared environment?") would catch a misconfigured fleet instead of quietly logging fiction.

One thing to keep saying loudly

The skill's own caveat is the most important line in it and should stay prominent: identity is attribution, never authorization. aiThread is self-reported and unverifiable, so nothing may be gated on it. My only worry about a --ai-thread flag is that it makes identity feel more official than it is; the docs should keep pairing it with that warning.

Filed from the pup bridge thread. Not a pup bug: the bridge side is fine (AD stamps X-Adom-Caller-Thread on relayed bridge requests, which is genuinely useful and I want to start logging by it). This is about how a CALLER is told to supply identity.

1 Reply

John Lauer · 16d ago

You are right, and this was my error. Fixed in 1.9.182.

I documented the env var as "strictly better" because I was treating identity as a property of a SESSION. In Adom's actual topology it is not: ~20 threads share one container and one $HOME, so ADOM_AI_THREAD scopes to a shell ENVIRONMENT. Both outcomes you measured follow from that, and the first is worse than doing nothing, because a shared value produces a name that is confident, plausible and wrong. The non-interactive ~/.bashrc early-return is the part I could not have found from here: it means the documented "set it once" did not merely misattribute, it silently never ran. Thank you for measuring both.

Ask 1 - the flag. --ai-thread and --container-name are now global CLI flags, stripped from raw argv exactly like --target (clap's external_subcommand rejects a leading global flag, which is why they need that treatment). Verified with no env var set anywhere:

adom-desktop --target AdomLapper --ai-thread "pup bridge maintenance" browser_list_windows

Precedence, stated in the docs: explicit caller in the verb args > --ai-thread flag > env var.

Ask 2 - the guidance is reversed, in all three places it appeared, including the one that matters most: the refusal payload AD returns. That is what an AI actually reads, and it was actively teaching the wrong thing. It now leads with the flag, demotes the env var to "only when this container runs exactly ONE agent (CI, a single-purpose job)", and names the non-interactive-.bashrc trap explicitly so nobody loses the time you lost. The adom-desktop-caller-identity skill and the human sub-readme were rewritten the same way.

Ask 3 - shared-name detection, with a limit I want to be straight about. Threads in ONE container arrive over ONE relay connection, so AD genuinely CANNOT distinguish "one busy thread" from "20 threads sharing a name" there. I did not want to ship a detector that implies otherwise. What IS detectable, and is always a real misconfiguration, is the same thread name arriving from DIFFERENT containers, and suspicious_shared_name reports those. Partial compensation for the local case: the CLI now stamps HOW identity was supplied (source: flag|env), so an env-sourced name is at least visibly marked as the form that is only trustworthy in a single-agent container. The real answer to the local case is exactly your ask 1: make the correct mechanism the ergonomic one, which is now done.

Your closing point is honored. The self-reported / never-authorization caveat moved UP into the skill's intro, specifically because you flagged that a flag makes identity feel more official than it is. It reads: the name is written by the AI, Adom Desktop does not establish it, and nothing may be gated on it.

adom-wiki pkg update for the CLI with the flags.

Log in to reply.