console

package
v1.76.5 Latest Latest
Warning

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

Go to latest
Published: Oct 13, 2026 License: MIT Imports: 43 Imported by: 0

Documentation

Overview

Package console is the GitHub module's half of the console: the handlers for connecting an organisation, the Apps view, the catalogue and runner Apps, the link App and people's links, moved out of the shared console so that the process which serves the console holds no App private key, no webhook secret and no connection record (docs/decisions/0071, v1.76 D-v176-1 b).

The console authenticates a person, takes a snapshot of who they are and calls these through the module's server (Register); the handlers act on that snapshot with the rules in internal/consoleauthz. A provider callback is still an HTTP route on the issuer, which verifies the signed state and the browser's cookie and forwards the one-time code to the module's `complete_*` method; the module never sees a session key.

Index

Constants

View Source
const FlowDeadline = 25 * time.Second

FlowDeadline bounds a call that finishes a provider's flow. The code the provider returned is single use, so the call is never retried and is given longer than an ordinary one: a lost answer after the exchange loses the key.

Variables

This section is empty.

Functions

func Register

func Register(s *modcall.Server, svc *Service)

Register makes the module answer the console's methods on s, to the console class alone (consolecall.Handle allows no other).

func TokensOf

TokensOf is the ring's last requests of one App, as the console tells them. It is shared by the module and by an issuer that keeps the ring itself: the ring is written where the decision to mint is made, which is the issuer.

func WithCaller

func WithCaller(ctx context.Context, c consolecall.Caller) context.Context

WithCaller is ctx with the snapshot of who the console authorized.

Types

type Catalogue

type Catalogue = catalogue.Catalogue

Catalogue is every App a deployment declares.

type Connections

type Connections = connection.Store

Connections is where connected organisations are kept: a record the console shows, a credential only the controller acts with.

type Deps

type Deps struct {
	// Log is where the module logs what is not an audit record; nil is the
	// default logger.
	Log *slog.Logger
	// Audit records what a person did through the console, as the actor of the
	// caller snapshot. Nil records nothing.
	Audit audit.Recorder
	// Trigger is told which organisation a write concerns, so the module's own
	// tick runs soon (docs/decisions/0029): the self-tick after a write. Nil
	// notifies nobody.
	Trigger port.Trigger
	// Policy is the policy in force: which organisations it binds.
	Policy func() *policy.Set
	// PublicURL is where a browser reaches the console, and RootURL the origin
	// the bootstrap callbacks are at; empty falls back to PublicURL.
	PublicURL, RootURL string
	// Orgs is where connected organisations are kept. Nil is a deployment
	// keeping no state in Kubernetes, which can connect none.
	Orgs Connections
	// OrgAppName is the name the organisation's App is created with, empty for
	// the default (<org>-access-roster). Nil is all defaults.
	OrgAppName func(org string) string
	// Reports is where the GitHub controller's reports are read. Nil is a
	// deployment keeping no state in Kubernetes, which has nowhere for a
	// controller to report to.
	Reports status.ReportReader
	// Confirmations is where operators confirm removal sets over the limit. Nil
	// confirms nothing.
	Confirmations connection.ConfirmationStore
	// LinkApp is where the link App is kept, and Links where people's links
	// are. Nil is a deployment where nobody can link an account.
	LinkApp link.AppStore
	Links   link.Links
	// LinkAppName is the name the link App is created with, empty for the
	// default (<owner>-access-roster-link).
	LinkAppName string
	// RunnerApps is where runner Apps are kept, and RunnerTiers the tiers an
	// operator may create an App for. Empty tiers keep no runner Apps.
	RunnerApps  runnerapp.Store
	RunnerTiers []string
	// CatalogueApps is where catalogue Apps are kept. Nil is a deployment
	// keeping no state in Kubernetes, which can create none.
	CatalogueApps catalogueapp.Store
	// Mints is the last installation tokens asked of each App, kept by the half
	// of the service that mints them. Nil is a deployment that mints none, or
	// whose mint is another process.
	Mints *mints.Ring
	// WebhookHTTP sends the signed ping a webhook rotation checks a new target
	// with. Nil is a client with a short timeout that follows no redirect.
	WebhookHTTP *http.Client
	// WebhookPingWindow is how long a rotation waits for the new target to
	// accept the new secret, and WebhookPingEvery how often it asks. Zero is
	// two minutes and five seconds.
	WebhookPingWindow, WebhookPingEvery time.Duration
	// Catalogue is every App the deployment declares. Nil declares none.
	Catalogue *catalogue.Catalogue
	// Places says where a deployment that keeps secrets in a store of the
	// ports (SSM, OpenBao) keeps an App's key, by the App's id in that store.
	// Nil is a deployment that keeps them in Kubernetes Secrets, which the
	// stores of internal/kube name themselves.
	Places Places
	// HTTP makes the calls to GitHub that connecting and disconnecting need.
	// Nil is a client with a short timeout.
	HTTP *http.Client
}

Deps is what the module's console handlers act on.

type MintRing

type MintRing = mints.Ring

MintRing is the last installation tokens asked of each App.

type Places

type Places interface {
	// GitHubAppPlace is the secrets adapter and the stable address beneath
	// which the key of the App with this id (`link`, a catalogue id,
	// `runner-<tier>-<org>`) is kept. ok is false when it is not known.
	GitHubAppPlace(id string) (adapter, location string, ok bool)
}

Places is where the secrets of the ports are kept.

type Service

type Service struct {
	// contains filtered or unexported fields
}

Service is the module's console handlers.

func New

func New(deps Deps) *Service

New returns the handlers over deps.

func (*Service) BeginGitHubAppConnect

func (c *Service) BeginGitHubAppConnect(ctx context.Context, r consolewire.BeginAppConnectRequest) (consolewire.Begun, error)

BeginGitHubAppConnect starts creating any App, or finishing installing one created before.

It hands the work to the same code the per-kind calls run, so the signed state, the cookie it is pinned to and the callback that finishes the flow are the ones that kind of App has always used: a browser part-way through a flow is unaffected by which call started it.

func (*Service) BeginGitHubCatalogueAppConnect

func (c *Service) BeginGitHubCatalogueAppConnect(ctx context.Context, id string) (consolewire.Begun, error)

BeginGitHubCatalogueAppConnect starts creating a catalogue App, or finishing installing one created before. The URL carries the state placeholder the issuer signs.

func (*Service) BeginGitHubConnect

func (c *Service) BeginGitHubConnect(ctx context.Context, org, requestedOwner string) (consolewire.Begun, error)

BeginGitHubConnect starts connecting an organisation.

Only one the policy binds. Connecting an organisation nothing will manage installs an App with members:write for no reason, and the likeliest cause is a typo in the login — which GitHub would answer with a 404 on its create page, after the operator has left this one.

The URL it returns carries consolewire.StatePlaceholder where the signed state belongs: the issuer signs it, with the caller as its actor.

func (*Service) BeginGitHubLinkAppConnect

func (c *Service) BeginGitHubLinkAppConnect(ctx context.Context, owner string) (consolewire.Begun, error)

BeginGitHubLinkAppConnect starts creating the link App. The URL carries the state placeholder the issuer signs.

func (*Service) BeginGitHubRunnerAppConnect

func (c *Service) BeginGitHubRunnerAppConnect(ctx context.Context, org, tier string) (consolewire.Begun, error)

BeginGitHubRunnerAppConnect starts creating a runner App, or finishing installing one created before.

Only in an organisation the policy binds, for the same reason as an organisation's App: a typo in the login would otherwise meet GitHub's 404 after the operator has left this page.

func (*Service) ChangeGitHubOrganisationOwner

ChangeGitHubOrganisationOwner changes the directory recorded as a connected organisation's owner, or removes it. The installation-wide operator alone: an owner that could hand its organisation to another, or take one, would make "who may operate this" a thing its operators decide.

func (*Service) CheckGitHubApp

CheckGitHubApp asks GitHub again about one App, whichever kind it is.

func (*Service) CheckGitHubCatalogueApp

CheckGitHubCatalogueApp asks GitHub again about one App.

func (*Service) CompleteGitHubCatalogueCreate

func (c *Service) CompleteGitHubCatalogueCreate(ctx context.Context, f consolewire.Flow) (consolewire.Continued, error)

CompleteGitHubCatalogueCreate is where GitHub sends the owner after creating a catalogue App, with a one-time code for its key. The key is kept and the owner is sent straight on to Install.

func (*Service) CompleteGitHubCatalogueSetup

func (c *Service) CompleteGitHubCatalogueSetup(ctx context.Context, f consolewire.Flow) (consolewire.Done, error)

CompleteGitHubCatalogueSetup is where GitHub sends the owner after installing a catalogue App. What is kept is where GitHub says the App is installed, asked as the App.

func (*Service) CompleteGitHubCreate

func (c *Service) CompleteGitHubCreate(ctx context.Context, f consolewire.Flow) (consolewire.Continued, error)

CompleteGitHubCreate is where GitHub sends the owner after Create, with a one-time code for the App's key. The key is kept and the owner is sent straight on to Install, under a fresh state: the one that brought them here is spent. The code is single use, so a lost answer after the exchange loses the key, as it always did: the call is not retried.

func (*Service) CompleteGitHubInstall

func (c *Service) CompleteGitHubInstall(ctx context.Context, f consolewire.Flow) (consolewire.Done, error)

CompleteGitHubInstall is where GitHub sends the owner after Install. The installation id in the query is a browser's word for it; what is kept is what GitHub says, asked as the App.

func (*Service) CompleteGitHubLinkApp

func (c *Service) CompleteGitHubLinkApp(ctx context.Context, f consolewire.Flow) (consolewire.Done, error)

CompleteGitHubLinkApp is where GitHub sends the owner after creating the link App. There is nothing to install: the App is used by being authorized, never by being installed.

func (*Service) CompleteGitHubRunnerCreate

func (c *Service) CompleteGitHubRunnerCreate(ctx context.Context, f consolewire.Flow) (consolewire.Continued, error)

CompleteGitHubRunnerCreate is where GitHub sends the owner after creating a runner App, with a one-time code for its key. The key is kept and the owner is sent straight on to Install.

func (*Service) CompleteGitHubRunnerSetup

func (c *Service) CompleteGitHubRunnerSetup(ctx context.Context, f consolewire.Flow) (consolewire.Done, error)

CompleteGitHubRunnerSetup is where GitHub sends the owner after installing a runner App. What is kept is where GitHub says the App is installed, asked as the App.

func (*Service) ConfirmGitHubRemovals

ConfirmGitHubRemovals lets exactly the removal set the operator saw go ahead. The fingerprint must be the one the organisation's latest report shows: confirming a set that has since changed would confirm people nobody looked at.

func (*Service) DisconnectGitHubApp

DisconnectGitHubApp uninstalls any App, then forgets it.

func (*Service) DisconnectGitHubCatalogueApp

DisconnectGitHubCatalogueApp uninstalls a catalogue App, then forgets it. A failed uninstall still forgets, and says what is left to do by hand. The App stays on GitHub: the API cannot delete one.

func (*Service) DisconnectGitHubLinkApp

DisconnectGitHubLinkApp forgets the link App, and makes every link made with it unverifiable — before forgetting, so that no pass can ever check one of those tokens with some later App's credentials and read GitHub's "unknown token" as a revocation.

func (*Service) DisconnectGitHubOrganisation

DisconnectGitHubOrganisation revokes, then forgets.

The revoke is uninstalling: GitHub's API cannot delete an App, so the registration stays for its owner to delete, and the response says where. Once uninstalled and forgotten nothing can act through it — nobody else ever held its key. A failed uninstall still forgets: the operator asked for this organisation to stop being managed, and the detail says what is left to do by hand.

func (*Service) DisconnectGitHubRunnerApp

DisconnectGitHubRunnerApp uninstalls a runner App, then forgets it. A failed uninstall still forgets, and says what is left to do by hand.

func (*Service) GetGitHubApp

GetGitHubApp implements one App's page.

func (*Service) GetGitHubStatus

GetGitHubStatus implements the GitHub page: every organisation the policy binds or the controller reports on, its bindings beside its report.

The join happens here rather than in the browser so that the page has one answer to render. Bindings come from the policy in force and reports from the controller's ConfigMap; neither is authoritative for the other, and the page shows where they disagree — a bound team the controller has not reported, a report for a team the policy no longer binds — rather than hiding either side.

A viewer of some directory sees the organisations that directory owns (the owner recorded when each was connected) and no others; the installation-wide viewer sees all. A report names the members of every bound team, which is more than a scoped viewer may see of another company. The link App and every link are people's across all organisations, so they are the installation-wide viewer's alone.

func (*Service) GitHubLinkComplete

func (c *Service) GitHubLinkComplete(ctx context.Context, r consolewire.LinkComplete) (consolewire.Linked, error)

GitHubLinkComplete finishes a link: GitHub's word on who the account is and which addresses it verifies, and the directory's word on which of those it knows. The issuer verified the signed state and the browser's cookie.

func (*Service) GitHubLinkLogins

func (c *Service) GitHubLinkLogins(ctx context.Context) (consolewire.LinkLogins, error)

GitHubLinkLogins indexes every address a link proves against the GitHub account that proves it, lowercased. Only a link that counts is in it: one GitHub still verifies an address for. Known is false where this module keeps no links.

func (*Service) GitHubLinkPage

func (c *Service) GitHubLinkPage(ctx context.Context) (consolewire.LinkPage, error)

GitHubLinkPage is where a person starts linking: the address GitHub's authorize page is at. The page has no session, so the call carries no caller; the issuer signs the state the URL carries and pins it to the person's browser.

ImportGitHubLinks adopts approved pairings as links, after three checks each: somebody approved the pairing, an address is a live account the directory vouches for, and the account is a member of a connected organisation. A link the person made is never displaced.

func (*Service) ListGitHubAppTokens

ListGitHubAppTokens implements an App's *Recent tokens*: the last installation tokens asked of it, minted or refused, newest first.

From this service's own memory of them rather than from the audit trail. The trail is the record and holds every request, but narrowing it by kind and target scans object after object of every hour in turn: measured against a real bucket the call was still running when the gateway gave up on it, and the section on the page was a sentence apologising. What it costs to keep the answer to this one question beside the process that answers it is a bounded ring per App.

Operator, not viewer: a request names who asked for it, as the Audit page does.

func (*Service) ListGitHubApps

ListGitHubApps implements the Apps list: every App in one shape.

func (*Service) RequestGitHubPass

RequestGitHubPass asks the GitHub controller to pass over now. The request is a marker the controller notices within its poll (see the controller's watch); a second one under a minute after the first is refused, so the button cannot be leaned on. It is recorded as a configuration change.

func (*Service) RotateGitHubAppWebhook

RotateGitHubAppWebhook replaces the secret a catalogue App's webhook deliveries are signed with.

Neither Argo CD nor Kargo accepts two secrets, so a rotation cannot overlap: the consumers must hold the new secret before GitHub signs with it, and GitHub holds exactly one. The order is therefore: keep the new secret where the consumers read it; wait until the (new) target accepts a ping signed with it, which proves the consumer reloaded; only then tell GitHub, the secret and a Kargo receiver's derived URL in one call. A failure before that restores the old secret, so deliveries go on exactly as they were.

func (*Service) SetClock

func (c *Service) SetClock(now func() time.Time)

SetClock sets the time the short cache of what GitHub says of an App is measured by, for a test; nil is the wall clock.

func (*Service) SetDeps

func (c *Service) SetDeps(d Deps)

SetDeps replaces the dependencies, keeping what the service remembers. It is for a test that builds the stores after the module.

Jump to

Keyboard shortcuts

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