Documentation
¶
Index ¶
- func DecryptField(dek []byte, value string) (string, error)
- func EncryptField(dek []byte, plaintext string) (string, error)
- func IsEncrypted(value string) bool
- func IsSensitiveKey(key string) bool
- type GitHubSyncClient
- func (c *GitHubSyncClient) GetFile(ctx context.Context, branch, path string) ([]byte, error)
- func (c *GitHubSyncClient) ListFiles(ctx context.Context, branch, prefix string) ([]string, error)
- func (c *GitHubSyncClient) PushFiles(ctx context.Context, branch, commitMessage string, files map[string][]byte) (string, error)
- type Handlers
- func (h *Handlers) GetName() string
- func (h *Handlers) Pull(c echo.Context) error
- func (h *Handlers) Push(c echo.Context) error
- func (h *Handlers) RegisterRoutes(e *echo.Echo, _ *app.Server) error
- func (h *Handlers) RotateKey(c echo.Context) error
- func (h *Handlers) SyncAll(c echo.Context) error
- func (h *Handlers) Syncer() *Syncer
- type LeaderWorker
- type PullRequest
- type PullResponse
- type PushRequest
- type PushResponse
- type RotateKeyResponse
- type SyncAllRequest
- type SyncAllResponse
- type SyncAllResult
- type SyncEncryptor
- type SyncEventRecord
- type SyncMeta
- type SyncMetaEnc
- type SyncSummary
- type Syncer
- func (s *Syncer) Pull(ctx context.Context, settingsName, userID string, deleteOrphans bool) (*PullResponse, error)
- func (s *Syncer) Push(ctx context.Context, settingsName, userID string, commitMessage string) (*PushResponse, error)
- func (s *Syncer) RotateKey(ctx context.Context, settingsName, userID string) (*RotateKeyResponse, error)
- func (s *Syncer) SyncAll(ctx context.Context, deleteOrphans bool, commitMessage string) (*SyncAllResponse, error)
- type Worker
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func DecryptField ¶
DecryptField decrypts a value produced by EncryptField. If the value does not have the enc:v1: prefix it is returned as-is (plaintext pass-through).
func EncryptField ¶
EncryptField encrypts a plaintext value with AES-256-GCM using a deterministic nonce. The nonce = HMAC-SHA256(DEK, plaintext)[:12] — same plaintext+DEK always yields the same ciphertext, keeping git diffs clean. Returns "enc:v1:<base64(nonce||ciphertext)>".
func IsEncrypted ¶
IsEncrypted returns true when the value carries the enc:v1: prefix.
func IsSensitiveKey ¶
IsSensitiveKey returns true when an env-var or header key name suggests its value is secret.
Types ¶
type GitHubSyncClient ¶
type GitHubSyncClient struct {
// contains filtered or unexported fields
}
GitHubSyncClient wraps the GitHub Git Data API for atomic batch push/pull.
func NewGitHubSyncClient ¶
func NewGitHubSyncClient(ctx context.Context, token, repoFullName string) (*GitHubSyncClient, error)
NewGitHubSyncClient creates a client authenticated with the given PAT. repoFullName must be "owner/repo". When the GITHUB_API environment variable is set, an Enterprise client is created using that URL so that operators who configure GitHub Enterprise in Helm do not need separate git-sync settings.
func (*GitHubSyncClient) GetFile ¶
GetFile returns the decoded content of a single file from the repository.
func (*GitHubSyncClient) ListFiles ¶
ListFiles returns repo-relative paths of all blobs under the given prefix on the branch.
func (*GitHubSyncClient) PushFiles ¶
func (c *GitHubSyncClient) PushFiles(ctx context.Context, branch, commitMessage string, files map[string][]byte) (string, error)
PushFiles commits files to the repository in a single atomic commit. files maps repo-relative paths to content. nil content entries delete the file. Returns the new commit SHA.
type Handlers ¶
type Handlers struct {
// contains filtered or unexported fields
}
Handlers implements app.CustomHandler for GitHub sync endpoints.
func NewHandlers ¶
func NewHandlers( settingsRepo portrepos.SettingsRepository, scheduleRepo schedule.Manager, webhookRepo portrepos.WebhookRepository, memoryRepo portrepos.MemoryRepository, taskRepo portrepos.TaskRepository, taskGroupRepo portrepos.TaskGroupRepository, userFileRepo portrepos.UserFileRepository, slackbotRepo portrepos.SlackBotRepository, kmsKeyARN, awsRegion string, githubAppInstallID string, ) *Handlers
NewHandlers creates a Handlers instance registering all required repositories. githubAppInstallID is optional — when set, it enables GitHub App token fallback for users who have not configured a personal GitHub token. kmsKeyARN and awsRegion are no longer used by Handlers directly; they are managed by the SettingsController and stored in settings.GitSync.Encryption.
func (*Handlers) RegisterRoutes ¶
RegisterRoutes implements app.CustomHandler.
type LeaderWorker ¶
type LeaderWorker struct {
// contains filtered or unexported fields
}
LeaderWorker wraps Worker with Kubernetes leader election so only one replica in the cluster runs the periodic sync at a time.
func NewLeaderWorker ¶
func NewLeaderWorker( syncer *Syncer, settingsRepo portrepos.SettingsRepository, interval time.Duration, client kubernetes.Interface, electionConfig schedule.LeaderElectionConfig, ) *LeaderWorker
NewLeaderWorker creates a LeaderWorker.
func (*LeaderWorker) Run ¶
func (lw *LeaderWorker) Run(ctx context.Context)
Run starts the leader election loop. Only the elected leader runs the sync worker.
type PullRequest ¶
type PullRequest struct {
// DeleteOrphans removes local resources that no longer exist in GitHub.
DeleteOrphans bool `json:"delete_orphans,omitempty"`
}
PullRequest is the HTTP request body for POST /sync/pull.
type PullResponse ¶
type PullResponse struct {
PulledAt time.Time `json:"pulled_at"`
Summary SyncSummary `json:"summary"`
}
PullResponse is the HTTP response body for POST /sync/pull.
type PushRequest ¶
type PushRequest struct {
// CommitMessage overrides the default commit message.
CommitMessage string `json:"commit_message,omitempty"`
}
PushRequest is the HTTP request body for POST /sync/push.
type PushResponse ¶
type PushResponse struct {
CommitSHA string `json:"commit_sha"`
PushedAt time.Time `json:"pushed_at"`
Summary SyncSummary `json:"summary"`
}
PushResponse is the HTTP response body for POST /sync/push.
type RotateKeyResponse ¶
type RotateKeyResponse struct {
CommitSHA string `json:"commit_sha"`
RotatedAt time.Time `json:"rotated_at"`
DEKVersion int `json:"dek_version"`
}
RotateKeyResponse is the HTTP response body for POST /sync/rotate-key.
type SyncAllRequest ¶ added in v1.402.0
type SyncAllRequest struct {
DeleteOrphans bool `json:"delete_orphans,omitempty"`
CommitMessage string `json:"commit_message,omitempty"`
}
SyncAllRequest is the HTTP request body for POST /sync/all.
type SyncAllResponse ¶ added in v1.402.0
type SyncAllResponse struct {
SyncedAt time.Time `json:"synced_at"`
Results []SyncAllResult `json:"results"`
}
SyncAllResponse summarises the result of syncing all tenants.
type SyncAllResult ¶ added in v1.402.0
type SyncAllResult struct {
SettingsName string `json:"settings_name"`
Direction string `json:"direction"`
Push *PushResponse `json:"push,omitempty"`
Pull *PullResponse `json:"pull,omitempty"`
Error string `json:"error,omitempty"`
}
SyncAllResult holds the result for a single settings tenant. Direction is "push" or "pull", determined automatically by comparing the remote .sync-meta.yaml syncedAt against the local LastPushedAt.
type SyncEncryptor ¶
type SyncEncryptor struct {
// contains filtered or unexported fields
}
SyncEncryptor manages fixed-DEK AWS KMS envelope encryption for GitHub sync. The DEK is generated once per GitSyncConfig and stored encrypted in Settings.
func NewSyncEncryptor ¶
func NewSyncEncryptor(ctx context.Context, kmsKeyARN, awsRegion string) (*SyncEncryptor, error)
NewSyncEncryptor creates a SyncEncryptor using AWS default credentials (IRSA > env vars > ~/.aws).
func (*SyncEncryptor) DecryptDEK ¶
DecryptDEK decrypts the base64-encoded encrypted DEK stored in GitSyncConfig.
func (*SyncEncryptor) GenerateAndEncryptDEK ¶
func (e *SyncEncryptor) GenerateAndEncryptDEK(ctx context.Context) (dek []byte, encryptedDEK string, err error)
GenerateAndEncryptDEK generates a fresh random 256-bit DEK, encrypts it with KMS, and returns both the raw DEK bytes (for immediate use) and the base64 encrypted DEK (for storage in GitSyncConfig.Encryption.EncryptedDEK).
type SyncEventRecord ¶
type SyncEventRecord struct {
At time.Time `json:"at"`
CommitSHA string `json:"commit_sha,omitempty"`
Summary SyncSummary `json:"summary"`
}
SyncEventRecord records the result of the last push or pull.
type SyncMeta ¶
type SyncMeta struct {
APIVersion string `yaml:"apiVersion"`
Kind string `yaml:"kind"`
SyncedAt time.Time `yaml:"syncedAt"`
Version string `yaml:"version"`
Encryption SyncMetaEnc `yaml:"encryption"`
}
SyncMeta is stored as .sync-meta.yaml at the rootPath in the GitHub repository. It records the last sync timestamp and encryption parameters (NOT the DEK itself).
type SyncMetaEnc ¶
type SyncMetaEnc struct {
Provider string `yaml:"provider"` // always "aws_kms"
KMSKeyARN string `yaml:"kmsKeyArn"`
Algorithm string `yaml:"algorithm"` // "AES-256-GCM"
DEKVersion int `yaml:"dekVersion"`
}
SyncMetaEnc holds encryption metadata stored in .sync-meta.yaml. The encryptedDEK is NOT stored here — it lives in Settings (K8s Secret).
type SyncSummary ¶
type SyncSummary struct {
FilesWritten int `json:"files_written"`
FilesDeleted int `json:"files_deleted"`
}
SyncSummary summarises what happened during a push or pull.
type Syncer ¶
type Syncer struct {
// contains filtered or unexported fields
}
Syncer orchestrates bidirectional GitHub sync for a user or team.
File layout in GitHub:
<rootPath>/<settingsName>/schedules/<id>.yaml <rootPath>/<settingsName>/webhooks/<id>.yaml <rootPath>/<settingsName>/settings.yaml <rootPath>/<settingsName>/files/<id>.yaml (personal only) <rootPath>/<settingsName>/slackbots/<id>.yaml <rootPath>/<settingsName>/.sync-meta.yaml
For personal sync settingsName == userID; for team sync settingsName is the team name. Each settings has its own subdirectory under rootPath so multiple settings can share the same repository without conflicting.
func NewSyncer ¶
func NewSyncer( settingsRepo portrepos.SettingsRepository, scheduleRepo schedule.Manager, webhookRepo portrepos.WebhookRepository, _ portrepos.MemoryRepository, _ portrepos.TaskRepository, _ portrepos.TaskGroupRepository, userFileRepo portrepos.UserFileRepository, slackbotRepo portrepos.SlackBotRepository, githubAppInstallID string, ) *Syncer
NewSyncer creates a Syncer. Non-nil repos are synced. githubAppInstallID is used to generate a GitHub App installation token when a user has no personal GitHub token configured (empty string disables fallback).
func (*Syncer) Pull ¶
func (s *Syncer) Pull(ctx context.Context, settingsName, userID string, deleteOrphans bool) (*PullResponse, error)
Pull downloads resources from GitHub and imports them.
func (*Syncer) Push ¶
func (s *Syncer) Push(ctx context.Context, settingsName, userID string, commitMessage string) (*PushResponse, error)
Push exports all resources for settingsName and commits them to GitHub.
func (*Syncer) RotateKey ¶
func (s *Syncer) RotateKey(ctx context.Context, settingsName, userID string) (*RotateKeyResponse, error)
RotateKey generates a fresh DEK, re-encrypts all GitHub files, and updates Settings.
func (*Syncer) SyncAll ¶ added in v1.402.0
func (s *Syncer) SyncAll(ctx context.Context, deleteOrphans bool, commitMessage string) (*SyncAllResponse, error)
SyncAll syncs all settings that have GitHub sync enabled. The direction (push/pull) is determined automatically per tenant by comparing the remote .sync-meta.yaml syncedAt against the local LastPushedAt: if GitHub is newer → pull; otherwise → push. Each tenant is processed independently; errors are captured in results without aborting others.
type Worker ¶
type Worker struct {
// contains filtered or unexported fields
}
Worker runs periodic bidirectional sync for all enabled git_sync configs.
Each settings is synced on a per-tenant schedule: the first sync time is offset by hash(settingsName) % interval so tenants don't hit GitHub simultaneously. Subsequent syncs happen at lastSyncedAt + interval.
Direction is determined by comparing the remote .sync-meta.yaml syncedAt timestamp against the locally stored LastPushedAt:
remoteSyncedAt > LastPushedAt → Pull (GitHub is newer) otherwise → Push (local is newer or equal)
func NewWorker ¶
func NewWorker(syncer *Syncer, settingsRepo portrepos.SettingsRepository, interval time.Duration) *Worker
NewWorker creates a Worker. interval must be > 0.