tarakan-client/internal/api/types.go
Maxfield Luke 58b29a7f84
Some checks failed
CI and releases / Test and vet (push) Failing after 1s
CI and releases / Build release binaries (push) Has been skipped
CI and releases / Publish GitHub release (push) Has been skipped
Initial commit on Forgejo
Fresh repository history for elektrine/tarakan-client hosted at
https://git.elektrine.com/elektrine/tarakan-client.
2026-07-29 04:56:36 -04:00

344 lines
13 KiB
Go

package api
import (
"fmt"
"net/url"
"strconv"
"strings"
)
// Repository is the canonical repository identity returned by Tarakan. Client
// code must compare this identity with the local origin before running a task.
type Repository struct {
ID int64 `json:"id,omitempty"`
Host string `json:"host,omitempty"`
Owner string `json:"owner"`
Name string `json:"name"`
FullName string `json:"full_name,omitempty"`
CanonicalURL string `json:"canonical_url,omitempty"`
ParticipationMode string `json:"participation_mode,omitempty"`
PrimaryLanguage string `json:"primary_language,omitempty"`
StarsCount int64 `json:"stars_count,omitempty"`
RecordURL string `json:"record_url,omitempty"`
}
func (r Repository) Slug() string {
if r.FullName != "" {
return r.FullName
}
if r.Owner == "" || r.Name == "" {
return ""
}
return r.Owner + "/" + r.Name
}
// Actor is the minimal public contributor identity returned by Tarakan.
type Actor struct {
ID int64 `json:"id,omitempty"`
Handle string `json:"handle,omitempty"`
}
type Lease struct {
ClaimedAt string `json:"claimed_at,omitempty"`
ExpiresAt string `json:"expires_at,omitempty"`
Active bool `json:"active"`
}
type Contribution struct {
ID int64 `json:"id,omitempty"`
Version int64 `json:"version,omitempty"`
Provenance string `json:"provenance"`
Summary string `json:"summary"`
Evidence string `json:"evidence,omitempty"`
Contributor *Actor `json:"contributor,omitempty"`
SubmittedAt string `json:"submitted_at,omitempty"`
}
type ReviewDecision struct {
ID int64 `json:"id,omitempty"`
Action string `json:"action"`
Reason string `json:"reason,omitempty"`
Evidence string `json:"evidence,omitempty"`
Reviewer *Actor `json:"reviewer,omitempty"`
DecidedAt string `json:"decided_at,omitempty"`
}
// Task is one immutable, commit-pinned unit of collaborative security work.
// ContractValue is what is currently escrowed against a job's repository. It
// is the answer to "is anyone paying for this?" at the moment the operator
// decides where to spend tokens.
type ContractValue struct {
Cents int64 `json:"cents"`
Credits int64 `json:"credits"`
Count int64 `json:"count"`
}
// Label renders the value for a queue listing, or "" when nothing is on offer.
func (c *ContractValue) Label() string {
if c == nil || c.Count == 0 {
return ""
}
switch {
case c.Cents > 0 && c.Credits > 0:
return fmt.Sprintf("$%d + %d credits", c.Cents/100, c.Credits)
case c.Cents > 0:
return fmt.Sprintf("$%d", c.Cents/100)
case c.Credits > 0:
return fmt.Sprintf("%d credits", c.Credits)
default:
return ""
}
}
type Task struct {
ID int64 `json:"id"`
Repository Repository `json:"repository"`
Contract *ContractValue `json:"contract,omitempty"`
CommitSHA string `json:"commit_sha"`
CommitCommittedAt string `json:"commit_committed_at,omitempty"`
Kind string `json:"kind"`
Capability string `json:"capability"`
Title string `json:"title"`
Description string `json:"description"`
Status string `json:"status"`
Visibility string `json:"visibility,omitempty"`
Creator *Actor `json:"creator,omitempty"`
Claimant *Actor `json:"claimant,omitempty"`
Reviewer *Actor `json:"reviewer,omitempty"`
Lease *Lease `json:"lease,omitempty"`
Contribution *Contribution `json:"contribution,omitempty"`
Contributions []Contribution `json:"contributions,omitempty"`
Decisions []ReviewDecision `json:"decisions,omitempty"`
PublishedAt string `json:"published_at,omitempty"`
SubmittedAt string `json:"submitted_at,omitempty"`
ReviewedAt string `json:"reviewed_at,omitempty"`
InsertedAt string `json:"inserted_at,omitempty"`
UpdatedAt string `json:"updated_at,omitempty"`
CompletedAt string `json:"completed_at,omitempty"`
DisclosedAt string `json:"disclosed_at,omitempty"`
Discloser *Actor `json:"discloser,omitempty"`
SensitiveReviewed bool `json:"sensitive_data_reviewed,omitempty"`
TaskURL string `json:"task_url,omitempty"`
RequestURL string `json:"request_url,omitempty"`
LinkedReviewID *int64 `json:"linked_review_id,omitempty"`
LinkedReview *LinkedReview `json:"linked_review,omitempty"`
TargetReviewID *int64 `json:"target_review_id,omitempty"`
TargetReview *LinkedReview `json:"target_review,omitempty"`
}
// LinkedReview is the structured Review created when completing a Request with
// Tarakan Review/Scan Format document.
type LinkedReview struct {
ID int64 `json:"id"`
ReviewStatus string `json:"review_status,omitempty"`
Visibility string `json:"visibility,omitempty"`
FindingsCount int64 `json:"findings_count,omitempty"`
Provenance string `json:"provenance,omitempty"`
ReviewKind string `json:"review_kind,omitempty"`
Model string `json:"model,omitempty"`
PromptVersion string `json:"prompt_version,omitempty"`
CommitSHA string `json:"commit_sha,omitempty"`
SourceRequestID *int64 `json:"source_request_id,omitempty"`
Findings []Finding `json:"findings,omitempty"`
}
// Submission completes a Request. Prefer Document (Review Format) for
// finding-producing kinds so Tarakan records Findings; Evidence is legacy prose.
// For verify_findings with target_review_id, set Verdict + Notes (or Summary).
type Submission struct {
Provenance string `json:"provenance"`
Summary string `json:"summary,omitempty"`
Evidence string `json:"evidence,omitempty"`
Model string `json:"model,omitempty"`
PromptVersion string `json:"prompt_version,omitempty"`
Document *ScanDocument `json:"document,omitempty"`
Verdict string `json:"verdict,omitempty"`
Notes string `json:"notes,omitempty"`
}
type Completion = Submission
// QueueRepository is a repository in the review queue returned by
// GET /api/repositories. It is the work a scanning client picks up.
type QueueRepository struct {
Host string `json:"host"`
Owner string `json:"owner"`
Name string `json:"name"`
Status string `json:"status"`
DefaultBranch string `json:"default_branch,omitempty"`
PrimaryLanguage string `json:"primary_language,omitempty"`
StarsCount int64 `json:"stars_count,omitempty"`
ScanCount int64 `json:"scan_count"`
LastScannedAt string `json:"last_scanned_at,omitempty"`
RegisteredAt string `json:"registered_at,omitempty"`
RecordURL string `json:"record_url,omitempty"`
}
// QueueFilter narrows jobs and repository discovery (stars, language, kind).
type QueueFilter struct {
MinStars int
Language string
Kind string
}
func (f QueueFilter) Empty() bool {
return f.MinStars <= 0 && strings.TrimSpace(f.Language) == "" && strings.TrimSpace(f.Kind) == ""
}
func (f QueueFilter) Query() url.Values {
values := url.Values{}
if f.MinStars > 0 {
values.Set("min_stars", strconv.FormatInt(int64(f.MinStars), 10))
}
if lang := strings.TrimSpace(f.Language); lang != "" {
values.Set("language", lang)
}
if kind := strings.TrimSpace(f.Kind); kind != "" {
values.Set("kind", kind)
}
return values
}
func (r QueueRepository) Slug() string {
if r.Owner == "" || r.Name == "" {
return ""
}
return r.Owner + "/" + r.Name
}
// Finding is one issue inside a review, visible only when the caller is
// authorized to see restricted evidence.
type Finding struct {
PublicID string `json:"public_id,omitempty"`
CanonicalFindingID string `json:"canonical_finding_id,omitempty"`
Disposition string `json:"disposition,omitempty"`
File string `json:"file"`
LineStart int64 `json:"line_start,omitempty"`
LineEnd int64 `json:"line_end,omitempty"`
Severity string `json:"severity"`
Title string `json:"title"`
Description string `json:"description"`
}
// ScanConfirmation is a recorded verdict on a review.
type ScanConfirmation struct {
Verdict string `json:"verdict"`
Provenance string `json:"provenance"`
Verifier string `json:"verifier,omitempty"`
}
// Scan is one submitted review of a repository at an exact commit.
type Scan struct {
ID int64 `json:"id"`
CommitSHA string `json:"commit_sha"`
Provenance string `json:"provenance"`
ReviewKind string `json:"review_kind"`
Model string `json:"model,omitempty"`
PromptVersion string `json:"prompt_version,omitempty"`
RunID string `json:"run_id,omitempty"`
ReviewStatus string `json:"review_status"`
Visibility string `json:"visibility"`
Verified bool `json:"verified"`
FindingsCount int64 `json:"findings_count"`
DetailsVisible bool `json:"details_visible"`
Submitter string `json:"submitter,omitempty"`
Findings []Finding `json:"findings,omitempty"`
Confirmations []ScanConfirmation `json:"confirmations,omitempty"`
}
// ScanDocument is the Tarakan Scan Format v1 body of a review submission.
type ScanDocument struct {
Format int64 `json:"tarakan_scan_format"`
Findings []ScanFinding `json:"findings"`
}
// ScanFinding is one finding inside a submitted ScanDocument.
type ScanFinding struct {
File string `json:"file"`
LineStart int64 `json:"line_start,omitempty"`
LineEnd int64 `json:"line_end,omitempty"`
Severity string `json:"severity"`
Title string `json:"title"`
Description string `json:"description"`
Disposition string `json:"disposition,omitempty"`
ExistingFindingID string `json:"existing_finding_id,omitempty"`
}
// ScanSubmission is the request body for POST .../scans.
type ScanSubmission struct {
CommitSHA string `json:"commit_sha"`
Provenance string `json:"provenance"`
ReviewKind string `json:"review_kind"`
Model string `json:"model,omitempty"`
PromptVersion string `json:"prompt_version,omitempty"`
RunID string `json:"run_id,omitempty"`
Document ScanDocument `json:"document"`
}
// RepositoryMemory is the compact canonical issue index used only after an
// agent has completed a blind discovery pass.
type RepositoryMemory struct {
Repository string `json:"repository"`
TargetCommitSHA string `json:"target_commit_sha,omitempty"`
Findings []CanonicalFindingMemory `json:"findings"`
Suppressions Suppressions `json:"suppressions"`
}
// Suppressions are findings the record already judged non-bugs. Reporting one
// again costs the operator tokens for a verdict that is already settled, so
// they are handed to the agent as things not to spend the budget rediscovering.
type Suppressions struct {
Note string `json:"note"`
Repository []Suppression `json:"repository"`
Patterns []Suppression `json:"patterns"`
}
type Suppression struct {
PublicID string `json:"public_id,omitempty"`
Fingerprint string `json:"fingerprint,omitempty"`
PatternKey string `json:"pattern_key,omitempty"`
File string `json:"file_path,omitempty"`
LineStart int64 `json:"line_start,omitempty"`
Title string `json:"title"`
DisputesCount int64 `json:"disputes_count,omitempty"`
DisputedRepositories int64 `json:"disputed_repositories,omitempty"`
Scope string `json:"scope"`
}
// Total is how many settled non-bugs this scan does not have to rediscover.
func (s Suppressions) Total() int { return len(s.Repository) + len(s.Patterns) }
type CanonicalFindingMemory struct {
PublicID string `json:"public_id"`
Status string `json:"status"`
File string `json:"file_path"`
LineStart int64 `json:"line_start,omitempty"`
LineEnd int64 `json:"line_end,omitempty"`
Severity string `json:"severity"`
Title string `json:"title"`
Description string `json:"description"`
FirstSeenCommitSHA string `json:"first_seen_commit_sha"`
LastSeenCommitSHA string `json:"last_seen_commit_sha"`
SameCommit bool `json:"same_commit"`
DetectionsCount int64 `json:"detections_count"`
DistinctSubmittersCount int64 `json:"distinct_submitters_count"`
DistinctModelsCount int64 `json:"distinct_models_count"`
ConfirmationsCount int64 `json:"confirmations_count"`
DisputesCount int64 `json:"disputes_count"`
}
type FindingVerdict struct {
CommitSHA string `json:"commit_sha"`
Verdict string `json:"verdict"`
Provenance string `json:"provenance"`
Notes string `json:"notes"`
Evidence string `json:"evidence,omitempty"`
}
// Verdict is the request body for POST .../scans/:id/verdict.
type Verdict struct {
Verdict string `json:"verdict"`
Provenance string `json:"provenance"`
Notes string `json:"notes"`
Evidence string `json:"evidence,omitempty"`
}