Documentation
¶
Overview ¶
Package config loads and saves ~/.gadak/config.json.
Credentials (email/token) share the file with the site settings, but they never reach the database, a log line, or a snapshot (constitution article 8). The file is written 0600.
Index ¶
- Constants
- func AttachmentDir() (string, error)
- func AttachmentDirFor(profile string) (string, error)
- func DBPath() (string, error)
- func DBPathFor(profile string) (string, error)
- func Dir() (string, error)
- func DirFor(profile string) (string, error)
- func Env(suffix string) string
- func LocalDBPath() (string, error)
- func LocalDBPathFor(profile string) (string, error)
- func Path() (string, error)
- func Profile() string
- func Profiles() ([]string, error)
- func RequireExistingProfile() error
- func SetProfile(name string)
- type Config
- func (c *Config) ApplyVerifiedIdentity(accountID, displayName, verifiedAt string)
- func (c *Config) EffectiveReconcileIntervalSec() int
- func (c *Config) EffectiveSyncIntervalSec() int
- func (c *Config) FieldSpecs() []FieldSpec
- func (c *Config) HasCredential() bool
- func (c *Config) NotifyEnabled() bool
- func (c *Config) Save() error
- func (c *Config) UpdateCheckEnabled() bool
- type ConfluenceConfig
- type FieldSpec
- type GroupRule
- type Member
- type Product
Constants ¶
const ( DefaultSyncIntervalSec = 60 // 1 minute DefaultReconcileIntervalSec = 3600 // 1 hour MinSyncIntervalSec = 15 // seconds MinReconcileIntervalSec = 300 // 5 minutes )
Sync loop defaults and floors. Zero in the file means "use default". Floors reject values that would thrash Jira or busy-loop the local process.
const ( // Name is the CLI binary, desktop app, and user-facing product name. Name = "gadak" // DirName is the directory under $HOME that holds the default profile. DirName = ".gadak" // DBFile is the SQLite filename inside a profile directory. DBFile = "gadak.db" // EnvPrefix is prepended to HOME, PROFILE, TOKEN, SITE, EMAIL, PROJECTS. EnvPrefix = "GADAK_" // Legacy names from the 2026-08 rename (scry → gadak). Still accepted so // an existing install keeps working until the user next launches gadak. LegacyName = "scry" LegacyDirName = ".scry" LegacyDBFile = "scry.db" LegacyEnvPrefix = "SCRY_" )
const LocalDBFile = "local.db"
LocalDBFile is the personal-history SQLite filename (visits/searches). It sits next to the mirror and is never sent to Jira or into a snapshot.
Variables ¶
This section is empty.
Functions ¶
func AttachmentDir ¶
AttachmentDir is where attachment bytes are cached, next to the mirror it belongs to (so a profile keeps its own, and deleting a profile takes its cache with it).
func AttachmentDirFor ¶
AttachmentDirFor is where attachment bytes are cached for the named profile.
func DirFor ¶
DirFor is the config directory for a named profile. "" or "default" means the root (GADAK_HOME / ~/.gadak); any other name lives under profiles/<name>. Names that fail validProfileName return an error and no path.
func Env ¶
Env returns GADAK_<suffix>, then SCRY_<suffix> if the new name is unset or empty. An empty GADAK_* value is treated as unset so a blank export cannot hide a real SCRY_* fallback (decision 0007: read SCRY_* when GADAK_* is unset).
func LocalDBPath ¶ added in v0.13.0
LocalDBPath is local.db for the active profile.
func LocalDBPathFor ¶ added in v0.13.0
LocalDBPathFor is local.db for the named profile (same directory as gadak.db).
func Profile ¶
func Profile() string
Profile returns the active profile name ("" for the default one).
func RequireExistingProfile ¶ added in v0.13.0
func RequireExistingProfile() error
RequireExistingProfile is the single owner of "may this named profile be used without creating it?". The default profile (empty / "default") is always allowed so first-run can mint ~/.gadak. A named profile whose directory does not exist is an error; names that do exist are listed so a typo is obvious.
func SetProfile ¶
func SetProfile(name string)
SetProfile is called by the CLI's --profile flag, which wins over the env var.
Types ¶
type Config ¶
type Config struct {
// The credential and what it connects to. Token is never copied out of this file.
Site string `json:"site,omitempty"` // https://your-site.atlassian.net
Email string `json:"email,omitempty"`
Token string `json:"token,omitempty"`
Projects []string `json:"projects,omitempty"`
// Result of verifying the credential: when `PUT credential/` last confirmed it
// against /myself, and who owns it. Unlike the token itself, both may be
// returned in a response.
TokenVerifiedAt string `json:"tokenVerifiedAt,omitempty"`
TokenOwner string `json:"tokenOwner,omitempty"`
// AccountID is the Jira accountId returned by /myself. Used for feed
// relevance (assignee/reporter/mention) and self-action filtering. Empty
// when the credential was never verified against a live site.
AccountID string `json:"account_id,omitempty"`
// Sync field mapping (contracts/sync.md, "Field mapping").
// Fields is the sole truth when present. FieldMap/EditableFields remain for
// legacy configs; FieldSpecs() synthesizes them into one shape.
Fields []FieldSpec `json:"fields,omitempty"`
FieldMap map[string]string `json:"fieldMap,omitempty"` // alias -> customfield_xxxxx (legacy)
BodyFields []string `json:"bodyFields,omitempty"` // ADF custom-field ids to fold into FTS
EditableFields map[string]string `json:"editableFields,omitempty"` // alias -> field id (legacy write allowlist)
// Optional surfaces carried over from the tool this was extracted from.
Members []Member `json:"members,omitempty"`
GroupRules []GroupRule `json:"groupRules,omitempty"`
GroupLabels map[string]string `json:"groupLabels,omitempty"`
GroupColors map[string]string `json:"groupColors,omitempty"`
ProductByGroup map[string]Product `json:"productByGroup,omitempty"`
Features map[string]bool `json:"features,omitempty"` // feed/push/deploy/qa/teamGroups
QaDashboardURL string `json:"qaDashboardUrl,omitempty"`
StaleThresholdHours int `json:"staleThresholdHours,omitempty"` // 0 = the client default (72)
// AttachmentCacheMB caps the on-disk attachment byte cache. 0 = package
// default (512 MB); a negative value is treated as 0.
AttachmentCacheMB int `json:"attachmentCacheMB,omitempty"`
// Sync periods in seconds. 0 means use DefaultSyncIntervalSec /
// DefaultReconcileIntervalSec. serve's Watch loop reads these once at start,
// so a change only takes effect after the process restarts.
SyncIntervalSec int `json:"syncIntervalSec,omitempty"`
ReconcileIntervalSec int `json:"reconcileIntervalSec,omitempty"`
// Notify enables OS desktop notifications from the sync watch loop after
// new personal-feed events. Default true when absent; set false to opt out.
// Pointer so omitempty can distinguish "unset" from explicit false.
Notify *bool `json:"notify,omitempty"`
// UpdateCheck enables the once-per-day GitHub release lookup that surfaces
// a newer version on sync/status/serve bootstrap. Default true when absent;
// set false to opt out (restores the prior "outbound is only Jira" model).
UpdateCheck *bool `json:"updateCheck,omitempty"`
// Confluence, when non-nil, enables the wiki-page mirror (second source).
// Spaces empty means every *global* space — not every space the account can
// see, which is what this comment used to claim and what a warning written
// from it went on to tell users. Cloud gives each person a personal space,
// so an unfiltered listing is mostly noise; personal spaces are mirrored
// only when named here. The rule itself lives in internal/sync/confluence.go.
Confluence *ConfluenceConfig `json:"confluence,omitempty"`
// contains filtered or unexported fields
}
func LoadFor ¶
LoadFor reads config.json for the named profile. Missing file returns an empty Config with dir set (not an error), matching Load's convention.
func (*Config) ApplyVerifiedIdentity ¶ added in v0.13.0
ApplyVerifiedIdentity stamps the three fields a successful Jira /myself call produces. CLI init uses this; the server onboarding path writes the same keys inline (internal/server/onboarding.go, write.go) — that package is outside this change's file boundary.
func (*Config) EffectiveReconcileIntervalSec ¶
EffectiveReconcileIntervalSec returns the reconcile interval Watch should use.
func (*Config) EffectiveSyncIntervalSec ¶
EffectiveSyncIntervalSec returns the interval Watch should use.
func (*Config) FieldSpecs ¶
FieldSpecs returns the effective field specs. Legacy configs that predate Fields carry FieldMap/EditableFields instead; synthesize specs from them so every consumer reads one shape. Fields, when present, is the sole truth.
func (*Config) HasCredential ¶
HasCredential reports whether writes and the attachment proxy are possible.
func (*Config) NotifyEnabled ¶
NotifyEnabled is true unless the user set notify: false. Absent means on.
func (*Config) Save ¶
Save writes the file atomically with mode 0600. When c.dir is set (LoadFor), the write goes to that profile's config.json; otherwise the active Path(). The profile directory (and ~/.gadak / GADAK_HOME when writing the default profile) is created and tightened to 0700; chmod failures are logged only.
func (*Config) UpdateCheckEnabled ¶
UpdateCheckEnabled is true unless the user set updateCheck: false. Absent means on.
type ConfluenceConfig ¶
type ConfluenceConfig struct {
Spaces []string `json:"spaces,omitempty"`
}
ConfluenceConfig is the optional wiki-page source. Presence (non-nil) is the on switch; same site/email/token as Jira, REST base under /wiki.
type FieldSpec ¶
type FieldSpec struct {
Alias string `json:"alias"` // stable key: ascii slug of the name, else cf_<id>
Label string `json:"label"` // Jira display name, in the account's language
IDs []string `json:"ids"` // all field ids sharing the name, most-filled first
Role string `json:"role"` // body | facet | user | plain
Kind string `json:"kind,omitempty"` // editor: option | multi_option | user | version_array | ""
Auto bool `json:"auto,omitempty"` // discovery-owned; regenerated on re-apply
}
FieldSpec is one logical custom field. Jira creates a separate field id per board template for the same concept, so one spec can carry several ids; the sync coalesces the first filled value (measured fact: 57 of 353 custom field names on one large site map to 2+ ids).
type GroupRule ¶
type GroupRule struct {
Group string `json:"group"`
Projects []string `json:"projects,omitempty"`
Labels []string `json:"labels,omitempty"`
Components []string `json:"components,omitempty"`
}
GroupRule classifies an issue into a group. Rules are read top-down and the first match wins. Conditions AND together; the list inside one condition ORs. An empty condition is always true.
type Member ¶
type Member struct {
Email string `json:"email"`
Name string `json:"name,omitempty"`
DisplayName string `json:"display_name,omitempty"`
Group string `json:"group,omitempty"`
Department string `json:"department,omitempty"`
JobRole string `json:"job_role,omitempty"`
JiraAccountID string `json:"jira_account_id,omitempty"`
AvatarURL string `json:"avatar_url,omitempty"`
}
Member is one entry of the static member directory injected through settings. It merges into bootstrap's members[], which is what gives an avatar its ring, tooltip, and team preset.