Who atgc acts as #
Every command that writes something publishes it under one account, and that is permanent: a pull request, an issue and an SSH key are all records in that account's own repository, signed by its credentials, and there is no edit that changes whose they were. So the question "who is atgc acting as right now" has to have an answer you can predict before you run anything, and one you can check afterwards.
This page is that answer. It matters more than it used to because one machine now routinely holds several accounts — a person's, and one for each agent working on that person's behalf.
Where the choice comes from #
Five things can decide it. The first one that answers, wins:
--accounton the command line. Lasts exactly one command.ATGC_ACCOUNTin the environment. Lasts as long as the shell does.- The checkout you are standing in, which names an account in its git
config as
user.email. This is also what git itself uses to author commits, so the commits and the records agree about who made them. - The default account, set by
atgc auth default. This is a fallback and nothing more — anything above it wins, every time. It is a person's: it must name an account that has a login of your own, and a command running as an agent never consults it. - The only account, when exactly one is logged in. Nothing to choose between, so nothing to configure.
If none of them answers, atgc stops and says so rather than guessing.
atgc auth status prints which account it would act as and which of these
decided it, and every command that writes says the same on its way past. If
the answer ever surprises you, that line names the thing to change.
A checkout that names an account you do not have #
atgc stops. It does not fall back to the next rule down.
This is deliberate and it is the most important thing on this page. A checkout
that states whose it is has stated whose it is; quietly publishing under
somebody else because that account happens to be logged in is how work ends up
attributed to the wrong person, and it is not recoverable afterwards. Log in as
the account the checkout names, or pass --account to override it for one
command.
Worktrees #
Git worktrees share one config file. git config --local inside a worktree
does not write anything local to that worktree — it writes the file every
worktree of that repository reads. So without help, five worktrees are five
copies of one identity, and whoever configured last configured all of them.
atgc repo configure inside a worktree writes that worktree's own config
instead, which git supports and which makes each worktree a different account
if you want it to be. Run it in the worktree, not in the main checkout:
cd path/to/worktree
atgc repo configure --account <handle|did>
It says which file it wrote, and --local forces the shared one when a repo
has a single owner and every worktree should be that owner.
A worktree never falls back to the default account. The default is a machine-wide preference for a checkout that says nothing about itself, and in a worktree that is the wrong answer: this is where an agent works, and inheriting whichever account the machine last defaulted to is how an agent publishes as the person who owns the laptop. An agent does not fall back to it anywhere, worktree or not — the rule is the plane, and the worktree rule is the same argument one step earlier. A worktree either names its account, or is on a machine with only one, or stops and asks.
A worktree that has not been given its own identity still works — it uses the repository's, like any other checkout. atgc says so when it does, because it means every worktree of that repository is the same account, which is usually not what somebody with several worktrees wanted.
Agents #
An agent's identity is an account like any other: it logs in, it owns its records, and it publishes under its own name. Three things differ.
An agent's login also joins Tangled. Tangled treats a
sh.tangled.actor.profile record at key self as membership: an account
without one is shown as "This user hasn't joined Tangled yet", and pull
requests it opens are never numbered or listed, even though their records are
valid and indexed. When an agent logs in and the record is absent, atgc writes
the lexicon's minimal profile (bluesky: false); a profile that is already
there is left untouched, because its avatar, bio and pinned repositories are
the account owner's. A person's login does not join on their behalf — that is
a decision about a public profile, not a side effect of wanting a credential.
It has no ~/.ssh. A person keeps push keys there, but an agent has no person
and no home directory of its own, and the machine's ~/.ssh belongs to
whoever owns the machine. atgc key create makes a key for the account and
keeps it where atgc keeps its other credentials.
It cannot use the default account. The default exists so a person does not
have to say who they are in their own checkouts; an agent names its account
explicitly, which is what configuring the worktree does. This is enforced in
three places rather than recommended: atgc auth default refuses to run as an
agent, it refuses to name an account that has no login of yours, and selection
ignores the pointer on the agent side. A machine whose first login was an
agent can therefore never end up with a fallback that resolves to that agent's
credentials.
Two ways to run several agents #
Several worktrees, one account. The agents work on your behalf, under your name, and there is nothing to configure: one account is the only account, so it answers everywhere. This is the simplest arrangement and it is what you get by default.
Several worktrees, several accounts. Each agent has its own identity:
atgc auth login <agent-handle|did> # once per identity
atgc key create # once per identity
cd path/to/worktree # once per worktree
atgc repo configure --account <agent-handle|did>
Now each agent's work is published under its own name, revocable on its own,
and visible as its own in any history. --account is needed on that last
command because it is the one command that deliberately ignores the checkout's
identity — it is writing the value the checkout will hold, so reading it first
would only ever re-affirm whatever was already there.
One account, two logins #
An agent working on your behalf can use your account without being you.
One account can be logged in twice: once for the person who owns it, and once for an agent. Both act as the same account and publish records that look identical, but they are separate grants — so an agent's can be replaced or revoked without touching yours, and either can be logged in again without disturbing the other.
atgc works out which is which when you log in, and writes it down then. An
agent logging itself in gets an agent's login; atgc auth login --agent <handle|did> makes one from your own terminal, for setting an agent up.
Which of the two a command uses is decided fresh each time, from the same signal. So a command run where an agent runs uses the agent's login, and one run from your own shell uses yours — and if the account has no login on that side, atgc stops rather than reaching for the other.
An agent asking for a person's login is refused. It would work — the credentials are valid and act as the same account — and that is exactly why: work published that way cannot be told from yours afterwards by anybody, including you. atgc stops and says to log in again, which costs one browser round trip and keeps the distinction.
That protection has one edge, and it is worth knowing rather than trusting past. atgc decides which of the two a command is from its environment, so an agent whose harness does not say so reads here as a person — and a person is handed a person's login without anything being wrong from where atgc is standing. Everything else fails the safe way: a command on a side the account has no login for is stopped, never quietly given the other. If it matters that an agent cannot reach your account at all, the way to get that is a separate account rather than a second login on yours.
atgc auth status lists the people's logins and says how many agents there
are; --agent lists those instead, and --all lists everything. On a machine
running a fleet that is the difference between a listing you can read and one
you cannot.
Listing agents costs nothing but reading two files. The handle beside each row
comes from the cache, and for an agent it is never re-read: an agent is named
by its DID, and DIDs do not change. A person's is re-read once the cached one
is a day old, which is the case anybody is actually debugging — a handle that
moved. --refresh re-reads every one of them, and how many came from the
cache is a note on stderr, so a listing never quietly stops checking without
saying so.