Documentation
¶
Overview ¶
Package githubid manages the daemon's set of GitHub identities: named (login, host, token) triples that islands clone and push as. The daemon is the credential owner, so islands work from any client device — including ones with no gh of their own. A client that does have gh can seed the store with `dejima auth push --github`. Each island selects one identity (or the default) at create time; the daemon materializes just that identity into the island's gh config (see HostsYAML), never the whole set.
Index ¶
- Constants
- func ConfigYAML() string
- func GitAuthor(id Identity) (name, email string)
- func GitConfig(id Identity) string
- func HostsYAML(id Identity) string
- func ValidateName(name string) error
- func VerifyToken(ctx context.Context, host, token string) (login string, id int64, err error)
- type Identity
- type Meta
- type Repo
- type RepoList
- type Store
- func (s *Store) DefaultFor(owner string) string
- func (s *Store) DeleteOwned(owner, name string) bool
- func (s *Store) Find(owner, name string) (Identity, bool)
- func (s *Store) List() []Meta
- func (s *Store) ListForOwner(owner string, ownsAll bool) []Meta
- func (s *Store) Put(id Identity)
- func (s *Store) PutOwned(id Identity)
- func (s *Store) Remove(name string) bool
- func (s *Store) Resolve(name string) (Identity, bool)
- func (s *Store) ResolveForIsland(islandOwner, name string) (Identity, bool)
- func (s *Store) Save() error
- func (s *Store) SetDefault(name string) error
- func (s *Store) SetDefaultFor(owner, name string) error
Constants ¶
const DefaultHost = "github.com"
DefaultHost is the GitHub host assumed when an identity doesn't name one.
Variables ¶
This section is empty.
Functions ¶
func ConfigYAML ¶
func ConfigYAML() string
ConfigYAML renders the gh config.yml that accompanies HostsYAML. Its only job is to carry the schema version marker: with it present, gh treats the materialized config as already-migrated and never tries to write to the read-only GH_CONFIG_DIR mount. Without it, gh runs a migration on first use and fails on the read-only dir (see HostsYAML).
func GitAuthor ¶
GitAuthor derives the git commit author (name, email) for an island that acts as this identity. Without it, commits inherit the host's gitconfig user.* — so a push authenticated as "work" gets authored with whatever email the daemon host happens to have, which GitHub then misattributes (it keys attribution off the email). We use GitHub's privacy-preserving noreply email so commits attribute to the right account without exposing a real address:
- canonical form (preferred): "<id>+<login>@users.noreply.<host>"
- fallback when the numeric id is unknown (identity stored before id capture): "<login>@users.noreply.<host>" — still account-linked, just the older form.
func GitConfig ¶
GitConfig renders a minimal gitconfig carrying just the identity's commit author. The daemon mounts this at /opt/host/gitconfig for identity-scoped islands (in place of the host's own gitconfig), and the entrypoint applies user.name/user.email from it — so authorship matches the push credential.
func HostsYAML ¶
HostsYAML renders the gh hosts.yml for a single identity — what gh reads from GH_CONFIG_DIR to authenticate git over HTTPS inside an island. Only ever one identity per file.
It MUST emit the modern multi-account schema (the per-user `users:` map), not just the legacy top-level form. gh migrates a legacy-only hosts.yml to this schema on first use, which means writing back to GH_CONFIG_DIR — but the daemon mounts that dir read-only, so the migration fails and `gh auth setup-git` errors out, leaving the island with no git credential helper (the clone then can't authenticate). Materializing the already-migrated form means gh has nothing to write. See also ConfigYAML, which supplies the version marker gh checks before deciding to migrate.
func ValidateName ¶ added in v0.8.17
ValidateName rejects an unusable identity name before it's stored.
func VerifyToken ¶
VerifyToken confirms a token authenticates against host and returns the login and numeric user id it belongs to. Called before storing an identity so a bad or expired token fails fast at `auth push` time instead of silently at clone/push time inside an island. The id feeds the canonical noreply commit email (see GitAuthor).
Types ¶
type Identity ¶
type Identity struct {
Name string `json:"name"` // dejima-local handle: "work", "personal", … (unique per Owner)
Login string `json:"login"` // GitHub username
ID int64 `json:"id,omitempty"` // GitHub numeric user id (for the canonical noreply commit email); 0 if unknown
Host string `json:"host"` // "github.com" or an enterprise host
Token string `json:"token"` // OAuth/PAT token — secret, never returned to clients
// Owner is the tenant that owns this identity ("" = a legacy/host identity).
// Server-authoritative: set from the authenticated caller, never client-forged.
// An identity is only ever materialized into islands of the same Owner (plus
// host-Shared identities into any island) — the containment invariant.
Owner string `json:"owner,omitempty"`
// (a team-wide org credential). Only the host owner may set it. Ignored on a
// non-host identity.
Shared bool `json:"shared,omitempty"`
}
Identity is one GitHub login the daemon can act as. Owner scopes it to a tenant so a team member can self-serve credentials for their OWN islands without the host owner having to hold a token for the member's private repos.
type Meta ¶
type Meta struct {
Name string `json:"name"`
Login string `json:"login"`
Host string `json:"host"`
Default bool `json:"default"`
Owner string `json:"owner,omitempty"`
}
Meta is an identity without its token: the safe view to hand back to clients.
type Repo ¶
type Repo struct {
NameWithOwner string `json:"name_with_owner"`
URL string `json:"url"` // https clone URL
Description string `json:"description"`
Private bool `json:"private"`
}
Repo is one repository visible to an identity, in the shape the island creator consumes: a full owner/name and the https clone URL.
type RepoList ¶
RepoList is a page of repositories plus whether the identity can see more than the page returned (the GitHub API advertises a next page via a Link header). Capped lets the UI say "showing the first N" honestly.
type Store ¶
type Store struct {
Default string `json:"default,omitempty"` // host owner's default identity name
Defaults map[string]string `json:"defaults,omitempty"` // tenant owner → default identity name
Idents []Identity `json:"idents,omitempty"` // owner-scoped identities
Identities map[string]Identity `json:"identities,omitempty"` // LEGACY map (bare-name keyed); migrated → Idents on load
}
Store is the per-daemon identity set. Identities are owner-scoped (a flat list, unique by (Owner, Name)). Default is the HOST owner's default name; Defaults holds each non-host tenant's default. The legacy Identities map is read on load and migrated into Idents (as host/"" identities), then dropped on save.
func Load ¶
Load reads the store under the lock — a consistent snapshot for read-only use. Returns an empty (non-nil) store if none exists yet.
func Update ¶
Update runs fn against the store under a process-wide lock and persists the result atomically. Use it for every read-modify-write — Put/Remove/SetDefault — so concurrent writers can't clobber each other (lost updates).
func (*Store) DefaultFor ¶ added in v0.8.17
DefaultFor returns owner's default identity name ("" if none). The host owner's default lives in Default (legacy-compatible); tenants' in Defaults.
func (*Store) DeleteOwned ¶ added in v0.8.17
DeleteOwned removes the (owner, name) identity, repointing that owner's default to a remaining identity of theirs (or clearing it).
func (*Store) Find ¶ added in v0.8.17
Find returns the (owner, name) identity (with token) — for handlers that need to check existence/ownership before a write.
func (*Store) ListForOwner ¶ added in v0.8.17
ListForOwner returns the identity metadata (no tokens) visible to owner ("" = host): their own identities, plus host-Shared ones. ownsAll (the host owner) sees everything.
func (*Store) Put ¶
Put adds/updates a host identity (its Owner is used as-is; the zero value "" is the host tenant).
func (*Store) PutOwned ¶ added in v0.8.17
PutOwned adds or updates the identity keyed by (Owner, Name). id.Owner must be set by the caller (server-authoritative). The first identity added for an owner becomes that owner's default.
func (*Store) ResolveForIsland ¶ added in v0.8.17
ResolveForIsland picks the identity to materialize into an island owned by islandOwner ("" = a host island), requesting name (or the owner's default when empty). THE CONTAINMENT CHOKEPOINT: a host island may use any host identity; a tenant island may use only its OWN identities or a host identity marked Shared. An operator's token can never reach another tenant's island.
func (*Store) SetDefault ¶
SetDefault sets the host default identity.
func (*Store) SetDefaultFor ¶ added in v0.8.17
SetDefaultFor marks (owner, name) as owner's default. Errors if owner has no identity by that name.