Documentation
¶
Index ¶
- func ParseInviteCode(raw string) (code, origin string, err error)
- func ValidateAPIOrigin(raw string) (string, error)
- type API
- type CredentialStore
- type Options
- type ResetOutcome
- type Result
- type Service
- func (s Service) Create(ctx context.Context, options Options) (Result, error)
- func (s Service) CreateAdditional(ctx context.Context, options Options, deviceID, token string) (Result, error)
- func (s Service) CreateOnNewBackend(ctx context.Context, options Options) (Result, error)
- func (s Service) CredentialState(ctx context.Context, configRoot string) (hosted.CredentialStatus, config.Config, error)
- func (s Service) Join(ctx context.Context, options Options, joinCode string) (Result, error)
- func (s Service) JoinAdditional(ctx context.Context, options Options, deviceID, token, joinCode string) (Result, error)
- func (s Service) JoinOnNewBackend(ctx context.Context, options Options, joinCode string) (Result, error)
- func (s Service) Reset(ctx context.Context, configRoot string, force bool) (ResetOutcome, error)
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ParseInviteCode ¶
ParseInviteCode extracts the invite code, and the backend origin it names, from whatever form the member pasted.
Three forms are accepted: the bare code, the desktop deep link, and the join-page URL whose fragment carries the code (a fragment never reaches server logs). Only the https form knows where the Project lives, so only it returns an origin; the other two return an empty one, meaning "the backend the caller selected". This is what lets a member on a purely local profile paste a link and join a team Project on a server this Mac has never used, without being asked which server that was.
func ValidateAPIOrigin ¶
ValidateAPIOrigin canonicalizes a backend origin a member supplied.
The rule is exactly hosted.New's, because that is the client every later call goes through: an HTTPS origin, or loopback HTTP for a backend running on this Mac, with no path, query, fragment, or userinfo. Checking it here means the member is told at the field rather than by the first request that fails.
Types ¶
type API ¶
type API interface {
CreateProject(context.Context, string, string, string, string) (hosted.Project, error)
CreateInvite(context.Context, string, int, int) (hosted.Invite, error)
Enroll(context.Context, string, string, string, string, string) (hosted.Enrollment, error)
JoinProject(context.Context, string, string, string, string, string) (hosted.Membership, error)
Bootstrap(context.Context) (hosted.Bootstrap, error)
CreateDashboardTicket(context.Context, string) (hosted.DashboardTicket, error)
RevokeDevice(context.Context, string) error
}
type CredentialStore ¶
type Options ¶
type Options struct {
ConfigRoot, RepositoryRoot, ProjectLabel, DeviceLabel, AppVersion string
// DisplayName is optional; empty means the member has not chosen one yet.
DisplayName string
// SkipInvite creates the Project without minting a one-use invite. A local
// Project has no second member to hand one to, and offering a code is how a
// screen implies that inviting somebody is the next step (ADR-072).
SkipInvite bool
}
type ResetOutcome ¶
type ResetOutcome struct {
Status hosted.CredentialStatus
BackendID string
APIBaseURL string
DeviceID string
ClearedWorkspaces int
CredentialDeleted bool
}
ResetOutcome describes what a reset changed on this device.
type Service ¶
type Service struct {
Backend config.Backend
Client func(token string) (API, error)
Creds CredentialStore
Register func(context.Context, string, string, string, config.Workspace) error
}
Service is one onboarding flow against one backend. The backend travels with the flow rather than with the profile: a Mac holds a device identity per backend (ADR-069, ADR-074), so "which server" and "which credential" are one question and are answered together.
func (Service) CreateAdditional ¶
func (s Service) CreateAdditional(ctx context.Context, options Options, deviceID, token string) (Result, error)
CreateAdditional creates another Project for a device that is already enrolled in the local profile. The existing credential is deliberately reused: one per-user service has one device identity across its Projects. Unlike first enrollment, a local registration failure must never revoke the shared device and strand its existing Projects.
func (Service) CreateOnNewBackend ¶
CreateOnNewBackend creates a Project on this flow's backend, whether or not the profile has used that backend before.
A backend the profile has never seen gets a device identity minted for it; one it already has an identity for reuses that identity, because a second credential for the same server would strand the Projects the first one holds. Both cases are one member action - "add a Project on this server" - so the choice is made here rather than in each caller.
func (Service) CredentialState ¶
func (s Service) CredentialState(ctx context.Context, configRoot string) (hosted.CredentialStatus, config.Config, error)
CredentialState reports whether the credential stored for this flow's backend is still accepted by it. A backend this profile has no device identity for is reported as OK because there is nothing that could have been rejected.
It is per backend, not per profile: one revoked team Project says nothing about the local Project beside it.
func (Service) JoinAdditional ¶
func (s Service) JoinAdditional(ctx context.Context, options Options, deviceID, token, joinCode string) (Result, error)
JoinAdditional redeems an invite for a device that is already enrolled in the local profile, adding a second Project without a second device identity.
It is the join counterpart of CreateAdditional and shares its rule: the existing credential is reused, and a local registration failure must never revoke the shared device and strand the Projects this Mac already has. That is why it does not route through Join, whose rollback revokes the device it just created - here that device is the one holding every other Project.
func (Service) JoinOnNewBackend ¶
func (s Service) JoinOnNewBackend(ctx context.Context, options Options, joinCode string) (Result, error)
JoinOnNewBackend redeems an invite on this flow's backend, minting a device identity for a backend the profile has never used and reusing the one it has otherwise. Joining a friend's team Project from a purely local profile is the first case, and it is the common one.
func (Service) Reset ¶
Reset forgets this device's identity on one backend so the member can enroll against it again. It deletes that backend's stored credential and removes the backend, its Projects, and the repositories registered to them. Every other backend on the profile is untouched, which is the whole point of binding Projects to backends: a revoked team Project must not take the local Project down with it.
It refuses unless that backend has actually rejected the credential. Being offline is not being locked out, and erasing a working enrollment cannot be undone. force exists for support and headless recovery, where the operator has already established that the enrollment is dead.