githubid

package
v0.7.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 30, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

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

View Source
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

func GitAuthor(id Identity) (name, email string)

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

func GitConfig(id Identity) string

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

func HostsYAML(id Identity) string

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 VerifyToken

func VerifyToken(ctx context.Context, host, token string) (login string, id int64, err error)

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", …
	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
}

Identity is one GitHub login the daemon can act as.

type Meta

type Meta struct {
	Name    string `json:"name"`
	Login   string `json:"login"`
	Host    string `json:"host"`
	Default bool   `json:"default"`
}

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

type RepoList struct {
	Repos  []Repo `json:"repos"`
	Capped bool   `json:"capped"`
}

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.

func ListRepos

func ListRepos(ctx context.Context, id Identity, limit int) (RepoList, error)

ListRepos returns repositories the identity can access (owner, collaborator, or org member), most-recently-pushed first, capped at limit (default/max 100, a single API page). The daemon owns this call so any client device — even one without gh — can browse before an island exists.

type Store

type Store struct {
	Default    string              `json:"default"`
	Identities map[string]Identity `json:"identities"`
}

Store is the per-daemon identity set with one default.

func Load

func Load() (*Store, error)

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

func Update(fn func(*Store) error) (*Store, error)

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) List

func (s *Store) List() []Meta

List returns identity metadata (no tokens), sorted by name.

func (*Store) Put

func (s *Store) Put(id Identity)

Put adds or updates an identity (keyed by Name). The first identity added becomes the default.

func (*Store) Remove

func (s *Store) Remove(name string) bool

Remove deletes an identity. If it was the default, the default falls to the first remaining identity (by name), or is cleared when none remain.

func (*Store) Resolve

func (s *Store) Resolve(name string) (Identity, bool)

Resolve returns the identity for name, or the default when name is empty. ok is false when nothing matches (unknown name, or empty name with no default).

func (*Store) Save

func (s *Store) Save() error

Save persists the store atomically at 0600 (it holds tokens).

func (*Store) SetDefault

func (s *Store) SetDefault(name string) error

SetDefault marks name as the default identity.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL