githubid

package
v0.8.65 Latest Latest
Warning

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

Go to latest
Published: Aug 13, 2026 License: Apache-2.0 Imports: 16 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 ValidateName added in v0.8.17

func ValidateName(name string) error

ValidateName rejects an unusable identity name before it's stored.

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", … (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"`
	// Shared marks a HOST identity as deliberately usable by every tenant's islands
	// (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"`
	Shared  bool   `json:"shared,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

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,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

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) DefaultFor added in v0.8.17

func (s *Store) DefaultFor(owner string) string

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

func (s *Store) DeleteOwned(owner, name string) bool

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

func (s *Store) Find(owner, name string) (Identity, bool)

Find returns the (owner, name) identity (with token) — for handlers that need to check existence/ownership before a write.

func (*Store) List

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

List returns host (+ host-shared) identity metadata.

func (*Store) ListForOwner added in v0.8.17

func (s *Store) ListForOwner(owner string, ownsAll bool) []Meta

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

func (s *Store) Put(id Identity)

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

func (s *Store) PutOwned(id Identity)

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

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

Remove deletes a host identity.

func (*Store) Resolve

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

Resolve resolves a host identity by name (or the host default when empty).

func (*Store) ResolveForIsland added in v0.8.17

func (s *Store) ResolveForIsland(islandOwner, name string) (Identity, bool)

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) 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 sets the host default identity.

func (*Store) SetDefaultFor added in v0.8.17

func (s *Store) SetDefaultFor(owner, name string) error

SetDefaultFor marks (owner, name) as owner's default. Errors if owner has no identity by that name.

Jump to

Keyboard shortcuts

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