How to Access Beads Storage and Issue Operations Using the Go API
The Beads Go library exposes a Storage interface in internal/storage/storage.go that provides CRUD operations, dependency management, and atomic transactions for issues through methods like CreateIssue, AddDependency, and RunInTransaction.
The gastownhall/beads repository ships a native Go package (github.com/steveyegge/beads) that wraps the underlying Dolt storage layer. You can access Beads storage and issue operations using the Go API to build automation, CI pipelines, or custom workflows that manage issues directly from Go code without invoking the CLI.
Opening a Beads Database Programmatically
Before executing operations, you must locate and open the database. The entry point is beads.go, which provides helper functions to discover the database path and initialize the storage layer.
Use FindDatabasePath() to walk up the directory tree searching for a .beads/*.db file, then pass the result to Open() to obtain a Storage instance:
ctx := context.Background()
dbPath := beads.FindDatabasePath()
if dbPath == "" {
log.Fatal("Beads DB not found – run `bd init` first")
}
store, err := beads.Open(ctx, dbPath)
if err != nil {
log.Fatalf("open storage: %v", err)
}
defer store.Close()
The Open function returns a concrete implementation of the Storage interface defined in internal/storage/storage.go. This interface is the sole contract you need to import; it maps every method call directly to SQL operations against the embedded Dolt database.
Creating and Modifying Issues
The Storage interface provides full CRUD capabilities for issues. Key methods include CreateIssue, GetIssue, UpdateIssue, CloseIssue, and DeleteIssue, all of which accept a context.Context and an actor string for auditability.
To create an issue programmatically, populate a beads.Issue struct (defined in internal/types/types.go) and call CreateIssue:
newIssue := &beads.Issue{
Title: "Programmatic demo issue",
Status: beads.StatusOpen,
Priority: 2,
IssueType: beads.TypeTask,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
if err := store.CreateIssue(ctx, newIssue, "go-demo"); err != nil {
log.Fatalf("create issue: %v", err)
}
For bulk imports, use CreateIssues to insert multiple records in a single call, reducing round-trips to the storage layer.
Managing Dependencies and Labels
Issue relationships are handled through the dependency subsystem. The methods AddDependency, RemoveDependency, GetDependencies, and GetDependents let you wire issues together using types like DepBlocks or DepDiscoveredFrom.
dep := &beads.Dependency{
IssueID: newIssue.ID,
DependsOnID: "bd-1",
Type: beads.DepDiscoveredFrom,
CreatedAt: time.Now(),
CreatedBy: "go-demo",
}
if err := store.AddDependency(ctx, dep, "go-demo"); err != nil {
log.Printf("dependency add failed: %v\n", err)
}
For categorization, AddLabel, RemoveLabel, and GetIssuesByLabel allow you to tag issues and filter collections programmatically.
Executing Atomic Workflows with Transactions
When you need to perform multiple operations atomically—such as creating an issue and immediately adding dependencies—use RunInTransaction. This method accepts a callback that receives a Transaction interface exposing the same methods as Storage, but guarantees that all calls within the callback commit together or rollback on error.
err := store.RunInTransaction(ctx, "create-with-deps", func(tx beads.Transaction) error {
if err := tx.CreateIssue(ctx, parent, "go-demo"); err != nil {
return err
}
if err := tx.CreateIssue(ctx, child, "go-demo"); err != nil {
return err
}
dep := &beads.Dependency{
IssueID: child.ID,
DependsOnID: parent.ID,
Type: beads.DepBlocks,
CreatedAt: time.Now(),
CreatedBy: "go-demo",
}
return tx.AddDependency(ctx, dep, "go-demo")
})
According to the source code in internal/storage/storage.go, the Transaction interface supports CreateIssue, UpdateIssue, AddDependency, and other mutating methods, making it ideal for consistent issue graph updates.
Querying Work Status and Statistics
The API includes specialized query methods for workflow automation. GetReadyWork returns open issues ready for scheduling, while GetBlockedIssues identifies items with unresolved dependencies.
ready, err := store.GetReadyWork(ctx, beads.WorkFilter{
Status: beads.StatusOpen,
Limit: 10,
})
if err != nil {
log.Fatalf("ready work: %v", err)
}
for _, i := range ready {
fmt.Printf("- %s: %s (prio %d)\n", i.ID, i.Title, i.Priority)
}
For dashboarding or reporting, GetStatistics returns global counts including TotalIssues, OpenIssues, and BlockedIssues without loading full issue records.
Summary
- Locate and open the database using
FindDatabasePathandOpenfrombeads.goto obtain aStoragehandle. - Perform CRUD operations through the
Storageinterface methods likeCreateIssue,UpdateIssue, andCloseIssuedefined ininternal/storage/storage.go. - Link issues with
AddDependencyand categorize withAddLabelusing types frominternal/types/types.go. - Ensure atomicity by wrapping multi-step workflows in
RunInTransactionto prevent partial updates. - Query efficiently using
GetReadyWorkfor scheduling andGetStatisticsfor metrics.
Frequently Asked Questions
How do I find the Beads database path automatically?
Call beads.FindDatabasePath(), which traverses parent directories looking for a .beads/*.db file. If it returns an empty string, initialize a new database with bd init before attempting to open it.
What is the difference between Storage and Transaction interfaces?
The Storage interface provides standalone methods that auto-commit, while the Transaction interface (accessed via RunInTransaction) groups multiple operations into a single atomic unit. If any step in the transaction callback returns an error, the entire batch rolls back.
Can I create multiple issues at once using the Go API?
Yes. Use the CreateIssues method on the Storage interface to batch insert issues in one call. This is more efficient than calling CreateIssue repeatedly when importing large datasets.
Where are the core data structures like Issue and Dependency defined?
The core types—including Issue, Dependency, Comment, and Statistics—are defined in internal/types/types.go. These structs are used throughout the public API in beads.go and the storage layer.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →