The distinction is not decorative: a Deploy key is expected to be
read-only and short-lived, and a UI that cannot tell a human from a CI
runner cannot show a useful key list.
A group is a principal like any other — it has an id, it can be the
grantee of a Grant at any scope and level, and grant/revoke/may
treat it no differently from a user (one implementation, no new grant type). What is
special is only that it holds no keys and is never authenticated as:
a group is a name that grants attach to, and the members of the group
inherit those grants. Membership is an edge, tracked beside the identity
table (AuthTable::add_to_group), not a field on the principal, so the
same principal record serves a group and a user.
There is exactly one principal of this kind, Principal::anonymous.
It is never stored in the identity table, it can never hold a key and it
can never hold a grant. It exists so that “a stranger clones a public
repository” is answered by AuthTable::may
like every other access question, rather than by a second code path
that could disagree with the first (one implementation).