1// SPDX-License-Identifier: AGPL-3.0-or-later
2
3package ports
4
5import (
6 "context"
7
8 "bnema.dev/ermite/internal/domain"
9)
10
11// CommitObjectReader reads raw commit objects (ADR 0030).
12type CommitObjectReader interface {
13 // ReadCommitObjects returns the raw content of each commit (without the
14 // "commit <size>\0" prefix), keyed by id. Ids that are missing, are not
15 // commits, or exceed domain.MaxCommitObjectBytes are left out of the map.
16 // The total size of the returned objects stays within
17 // domain.MaxCommitObjectsBytes: ids are read in order, and the first one
18 // that would exceed the budget ends the read, so it and the ids after it
19 // are left out (the caller leaves those commits unchecked). A cancelled
20 // ctx is returned as its error.
21 // domain.ErrNotFound when the repository has no directory.
22 ReadCommitObjects(ctx context.Context, repo domain.RepoName, ids []domain.ObjectID) (map[domain.ObjectID][]byte, error)
23}
24
25// KeyInspector parses public keys for the trusted key list.
26type KeyInspector interface {
27 // Inspect parses a public key of the given format and returns its ID
28 // (fingerprint) and its canonical text: the "<type> <base64>" part of an
29 // authorized_keys line for SSH (comment dropped), the armored public key
30 // for GPG (exactly one entity, private material refused). Any parse
31 // failure wraps domain.ErrInvalid.
32 Inspect(format domain.SignatureFormat, publicKey string) (id, canonical string, err error)
33}
34
35// SignatureVerifier checks one signature against a set of trusted keys. It
36// never errors: every failure is a status.
37type SignatureVerifier interface {
38 // Verify checks signature over payload, signed by committerEmail.
39 // signedAt is the committer date: the committer chooses it, and it is
40 // authenticated as part of the payload but is not a trusted timestamp.
41 // SSH ignores it, and so does GPG, which
42 // judges key and signature expiry at the signature creation time and
43 // revocation at the current time. keys are the trusted keys of the
44 // verifier's format. It returns Format, Status, Fingerprint, and
45 // KeyID/KeyTitle of the key that verified (even on email_mismatch).
46 Verify(payload, signature []byte, committerEmail string, signedAt int64, keys []domain.TrustedKey) domain.CommitVerification
47}
48
49// TrustedKeyStore keeps the trusted keys (ADR 0030). It does not bound their
50// number: the use case checks domain.MaxTrustedKeys.
51type TrustedKeyStore interface {
52 // ListTrustedKeys returns every stored key, oldest first.
53 ListTrustedKeys(ctx context.Context) ([]domain.TrustedKey, error)
54 // CreateTrustedKey stores a key; domain.ErrAlreadyExists if the ID is taken.
55 CreateTrustedKey(ctx context.Context, k domain.TrustedKey) error
56 // DeleteTrustedKey removes a key or returns domain.ErrNotFound.
57 DeleteTrustedKey(ctx context.Context, id string) error
58}
59
60// TrustedKeyService manages the trusted keys. *usecase.Signatures implements it.
61type TrustedKeyService interface {
62 // ListTrustedKeys lists the keys, the builtin instance key first. Any
63 // authenticated caller may.
64 ListTrustedKeys(ctx context.Context, p domain.Principal) ([]domain.TrustedKey, error)
65 // AddTrustedKey adds a key. Owner only.
66 AddTrustedKey(ctx context.Context, p domain.Principal, format domain.SignatureFormat, title, publicKey string, emails []string) (domain.TrustedKey, error)
67 // RemoveTrustedKey removes a key by ID. Owner only; the builtin key is
68 // refused with domain.ErrPreconditionFailed.
69 RemoveTrustedKey(ctx context.Context, p domain.Principal, id string) error
70}
71
72// CommitVerifier tells the signature state of commits the caller already read.
73// *usecase.Signatures implements it.
74type CommitVerifier interface {
75 // VerifyCommits returns one verification per commit, keyed by id. A
76 // commit that could not be read is left out (unchecked).
77 VerifyCommits(ctx context.Context, repo domain.RepoName, commits []domain.Commit) map[domain.ObjectID]domain.CommitVerification
78}