Documentation
¶
Overview ¶
Package selfsource packages a Go module's source, docs, and a build-time code index into one deterministic blob a binary can embed and serve — debuginfo for agents.
A bundle is a gzip'd tar archive with three kinds of entries:
manifest.json build identity (module path, VCS revision, build time) index.json code index (packages, exported symbols, references) files/<path> verbatim source and doc files, module-relative paths
Encoding is byte-deterministic: same inputs produce the same blob.
Index ¶
- Constants
- Variables
- func MatchPath(pattern, p string) bool
- func SplitLines(content []byte) []string
- func SplitRef(ref string) (path, line, col string)
- type AmbiguousError
- type Bundle
- type Frame
- type Index
- type Manifest
- type Match
- type ModuleDep
- type Package
- type Reader
- func (r *Reader) AnnotateStack(trace []byte, contextLines int) (string, error)
- func (r *Reader) Deps(name string) ([]string, error)
- func (r *Reader) ExecTool(name string, input []byte) (string, error)
- func (r *Reader) Extract(dir string) error
- func (r *Reader) Files() []string
- func (r *Reader) Importers(name string) ([]string, error)
- func (r *Reader) Manifest() Manifest
- func (r *Reader) Package(name string) (Package, error)
- func (r *Reader) Packages() []Package
- func (r *Reader) Read(name string) (string, []byte, error)
- func (r *Reader) ResolveTracePath(file string) (string, bool)
- func (r *Reader) Search(pattern string, pathGlobs []string) ([]Match, error)
- func (r *Reader) Symbol(name string) (Symbol, error)
- func (r *Reader) SymbolForFrame(fn string) (Symbol, error)
- func (r *Reader) Symbols() []Symbol
- type Symbol
Examples ¶
Constants ¶
const DefaultMaxDecodedBytes = 512 << 20 // 512 MiB
DefaultMaxDecodedBytes caps the total decompressed size Decode will accept. A bundle is a compressed archive, so a small blob can expand enormously; reading a bundle you did not build is a documented workflow, and an unbounded reader would make that an out-of-memory vector.
const FormatV1 = "selfsource/v1"
FormatV1 identifies the first bundle format version.
Variables ¶
var ErrNotFound = errors.New("not found")
ErrNotFound is wrapped by lookups that matched nothing.
var ErrTooLarge = errors.New("selfsource: bundle expands beyond the decode limit; raise it with DecodeLimit if the bundle is trusted")
ErrTooLarge reports a bundle whose decompressed size exceeds the budget.
Functions ¶
func MatchPath ¶
MatchPath reports whether a slash-separated path matches a glob pattern. Patterns use path.Match per segment, plus "**" matching any number of segments (including zero). Patterns are anchored: "*.go" matches only root-level files; use "**/*.go" for the whole tree.
func SplitLines ¶
SplitLines splits file content into lines without the phantom trailing empty line that strings.Split yields for newline-terminated content.
Types ¶
type AmbiguousError ¶
AmbiguousError reports a loose lookup that matched more than one candidate.
func (*AmbiguousError) Error ¶
func (e *AmbiguousError) Error() string
type Bundle ¶
type Bundle struct {
Manifest Manifest
Index Index
Files map[string][]byte // module-relative slash paths
}
Bundle is the in-memory form, pre-encoding.
type Frame ¶
type Frame struct {
Func string
File string // as printed in the trace (usually absolute build path)
Line int
}
Frame is one resolved stack-trace frame.
func ParseStack ¶
ParseStack extracts frames from a Go panic message or goroutine dump. Unrecognized lines are skipped, so raw crash output can be fed verbatim.
Example ¶
package main
import (
"fmt"
"github.com/oplehto/selfsource-go"
)
func main() {
trace := []byte(`panic: runtime error: invalid memory address or nil pointer dereference
goroutine 1 [running]:
example.com/demo.Greet(...)
/build/demo.go:4 +0x18
main.main()
/build/main.go:10 +0x2c
`)
for _, f := range selfsource.ParseStack(trace) {
fmt.Printf("%s at %s:%d\n", f.Func, f.File, f.Line)
}
}
Output: example.com/demo.Greet at /build/demo.go:4 main.main at /build/main.go:10
type Manifest ¶
type Manifest struct {
Format string `json:"format"`
ModulePath string `json:"module_path"`
Revision string `json:"revision"`
BuildTime string `json:"build_time"`
// GoVersion is the toolchain that generated the index (e.g. "go1.26.0").
GoVersion string `json:"go_version,omitempty"`
// Deps lists resolved module requirements, sorted by path. Same
// disclosure as go.sum — exclude via config if that matters to you.
Deps []ModuleDep `json:"deps,omitempty"`
}
Manifest records the identity of the build a bundle was generated from.
Every field is deliberately host-independent: no filesystem paths, user names, or environment values. A bundle ships inside a binary that may be handed to anyone, so it describes the CODE, never the machine that compiled it. TestNoHostPathsLeak enforces this end to end.
type ModuleDep ¶
ModuleDep is one resolved module requirement. Deliberately carries no local directory: that would be a host path.
type Package ¶
type Package struct {
ImportPath string `json:"import_path"`
Dir string `json:"dir"`
Files []string `json:"files"`
Imports []string `json:"imports"`
}
Package is one module-local package in the index.
type Reader ¶
type Reader struct {
// contains filtered or unexported fields
}
Reader answers queries over a decoded bundle.
func Decode ¶
Decode parses an encoded bundle, accepting at most DefaultMaxDecodedBytes of decompressed content.
Example ¶
package main
import (
"fmt"
"github.com/oplehto/selfsource-go"
)
// exampleBundle builds a tiny two-file bundle in memory. Real bundles come
// from the gen package (or `selfsource gen`) at build time and reach the
// binary via go:embed.
func exampleBundle() *selfsource.Reader {
b := &selfsource.Bundle{
Manifest: selfsource.Manifest{
Format: selfsource.FormatV1,
ModulePath: "example.com/demo",
Revision: "0123abc",
BuildTime: "2026-01-01T00:00:00Z",
},
Index: selfsource.Index{
Packages: []selfsource.Package{
{ImportPath: "example.com/demo", Dir: ".", Files: []string{"demo.go"}, Imports: []string{"fmt"}},
},
Symbols: []selfsource.Symbol{
{
Package: "example.com/demo", Name: "Greet", Kind: "func",
Signature: "func Greet(name string) string",
Doc: "Greet says hello.",
Def: "demo.go:4",
Refs: []string{"demo_test.go:9:8"},
Exported: true,
},
},
},
Files: map[string][]byte{
"demo.go": []byte("package demo\n\n// Greet says hello.\nfunc Greet(name string) string { return \"Hello, \" + name }\n"),
"README.md": []byte("# demo\n"),
},
}
data, err := b.Encode()
if err != nil {
panic(err)
}
r, err := selfsource.Decode(data)
if err != nil {
panic(err)
}
return r
}
func main() {
r := exampleBundle()
m := r.Manifest()
fmt.Println(m.ModulePath, "at", m.Revision)
fmt.Println(r.Files())
}
Output: example.com/demo at 0123abc [README.md demo.go]
func DecodeLimit ¶
DecodeLimit is Decode with an explicit decompressed-size budget. A limit of zero or less means unlimited — only for bundles you produced yourself.
func (*Reader) AnnotateStack ¶
AnnotateStack renders a stack trace as a report: each frame that resolves into the bundle is shown with contextLines of surrounding source and, when the function is indexed, its doc comment. Frames outside the bundle (runtime, stdlib, dependencies) are listed but marked as such.
func (*Reader) ExecTool ¶
ExecTool runs one named bundle query with a JSON input object — the shared executor behind the CLI chat loop and the MCP server, exported so host binaries can wire the same surface into their own agent frontends.
Tools and inputs: info {}, list_files {}, read_file {path}, search {pattern, glob?}, symbol {name}, references {name}, deps {package, reverse?}, annotate_stack {trace, context?}.
func (*Reader) Extract ¶
Extract writes the bundle's files into dir, recreating the module-relative layout — for tools that need source on disk (go tool pprof -source_path, diffing against a checkout, rebuilding). The destination must not exist or must be an empty directory, and must not be a symlink: Extract never overwrites existing content and never follows a link out of the path you named.
func (*Reader) Read ¶
Read returns a file's resolved path and content. The name may be the exact path, a path suffix, or a bare basename; an ambiguous name is an error.
Example ¶
package main
import (
"fmt"
"github.com/oplehto/selfsource-go"
)
// exampleBundle builds a tiny two-file bundle in memory. Real bundles come
// from the gen package (or `selfsource gen`) at build time and reach the
// binary via go:embed.
func exampleBundle() *selfsource.Reader {
b := &selfsource.Bundle{
Manifest: selfsource.Manifest{
Format: selfsource.FormatV1,
ModulePath: "example.com/demo",
Revision: "0123abc",
BuildTime: "2026-01-01T00:00:00Z",
},
Index: selfsource.Index{
Packages: []selfsource.Package{
{ImportPath: "example.com/demo", Dir: ".", Files: []string{"demo.go"}, Imports: []string{"fmt"}},
},
Symbols: []selfsource.Symbol{
{
Package: "example.com/demo", Name: "Greet", Kind: "func",
Signature: "func Greet(name string) string",
Doc: "Greet says hello.",
Def: "demo.go:4",
Refs: []string{"demo_test.go:9:8"},
Exported: true,
},
},
},
Files: map[string][]byte{
"demo.go": []byte("package demo\n\n// Greet says hello.\nfunc Greet(name string) string { return \"Hello, \" + name }\n"),
"README.md": []byte("# demo\n"),
},
}
data, err := b.Encode()
if err != nil {
panic(err)
}
r, err := selfsource.Decode(data)
if err != nil {
panic(err)
}
return r
}
func main() {
r := exampleBundle()
// Loose matching: a bare basename resolves when unique.
path, content, err := r.Read("demo.go")
if err != nil {
panic(err)
}
fmt.Printf("%s is %d bytes\n", path, len(content))
}
Output: demo.go is 94 bytes
func (*Reader) ResolveTracePath ¶
ResolveTracePath maps a file path as the runtime prints it — an absolute build-machine path, or a module-relative one under -trimpath — onto a bundled path. The shared join fallback for stack reports and pprof frames.
func (*Reader) Search ¶
Search runs a regexp over bundled files. Empty pathGlobs means all files; otherwise a file is searched when any glob matches its path. A glob set that matches no file at all is an error rather than an empty result — a silently-empty search reads as "no matches" and hides the typo.
Example ¶
package main
import (
"fmt"
"github.com/oplehto/selfsource-go"
)
// exampleBundle builds a tiny two-file bundle in memory. Real bundles come
// from the gen package (or `selfsource gen`) at build time and reach the
// binary via go:embed.
func exampleBundle() *selfsource.Reader {
b := &selfsource.Bundle{
Manifest: selfsource.Manifest{
Format: selfsource.FormatV1,
ModulePath: "example.com/demo",
Revision: "0123abc",
BuildTime: "2026-01-01T00:00:00Z",
},
Index: selfsource.Index{
Packages: []selfsource.Package{
{ImportPath: "example.com/demo", Dir: ".", Files: []string{"demo.go"}, Imports: []string{"fmt"}},
},
Symbols: []selfsource.Symbol{
{
Package: "example.com/demo", Name: "Greet", Kind: "func",
Signature: "func Greet(name string) string",
Doc: "Greet says hello.",
Def: "demo.go:4",
Refs: []string{"demo_test.go:9:8"},
Exported: true,
},
},
},
Files: map[string][]byte{
"demo.go": []byte("package demo\n\n// Greet says hello.\nfunc Greet(name string) string { return \"Hello, \" + name }\n"),
"README.md": []byte("# demo\n"),
},
}
data, err := b.Encode()
if err != nil {
panic(err)
}
r, err := selfsource.Decode(data)
if err != nil {
panic(err)
}
return r
}
func main() {
r := exampleBundle()
matches, err := r.Search(`func \w+`, []string{"**/*.go"})
if err != nil {
panic(err)
}
for _, m := range matches {
fmt.Printf("%s:%d: %s\n", m.Path, m.Line, m.Text)
}
}
Output: demo.go:4: func Greet(name string) string { return "Hello, " + name }
func (*Reader) Symbol ¶
Symbol resolves a symbol loosely: "pkg.Name", "pkgsuffix.Name", "Name", or "Type.Method" forms all work when unique.
Example ¶
package main
import (
"fmt"
"github.com/oplehto/selfsource-go"
)
// exampleBundle builds a tiny two-file bundle in memory. Real bundles come
// from the gen package (or `selfsource gen`) at build time and reach the
// binary via go:embed.
func exampleBundle() *selfsource.Reader {
b := &selfsource.Bundle{
Manifest: selfsource.Manifest{
Format: selfsource.FormatV1,
ModulePath: "example.com/demo",
Revision: "0123abc",
BuildTime: "2026-01-01T00:00:00Z",
},
Index: selfsource.Index{
Packages: []selfsource.Package{
{ImportPath: "example.com/demo", Dir: ".", Files: []string{"demo.go"}, Imports: []string{"fmt"}},
},
Symbols: []selfsource.Symbol{
{
Package: "example.com/demo", Name: "Greet", Kind: "func",
Signature: "func Greet(name string) string",
Doc: "Greet says hello.",
Def: "demo.go:4",
Refs: []string{"demo_test.go:9:8"},
Exported: true,
},
},
},
Files: map[string][]byte{
"demo.go": []byte("package demo\n\n// Greet says hello.\nfunc Greet(name string) string { return \"Hello, \" + name }\n"),
"README.md": []byte("# demo\n"),
},
}
data, err := b.Encode()
if err != nil {
panic(err)
}
r, err := selfsource.Decode(data)
if err != nil {
panic(err)
}
return r
}
func main() {
r := exampleBundle()
sym, err := r.Symbol("Greet")
if err != nil {
panic(err)
}
fmt.Println(sym.Signature)
fmt.Println("defined at", sym.Def, "referenced from", sym.Refs[0])
}
Output: func Greet(name string) string defined at demo.go:4 referenced from demo_test.go:9:8
func (*Reader) SymbolForFrame ¶
SymbolForFrame resolves a runtime-style function name — the form stack traces and pprof profiles print, like "pkg/path.(*Type).Method", "pkg.Func[go.shape.int]", or "pkg.Func.func1" — to its indexed symbol. The primary join key between runtime data and the bundle.
The frame's package part constrains the match: "internal/stringslite.Index" must NOT resolve to a module type that happens to be named Index. Loose bare-name lookups are Reader.Symbol's job, for humans; frames carry a package and it is checked.
type Symbol ¶
type Symbol struct {
Package string `json:"package"` // full import path
Name string `json:"name"` // "Name" or "Type.Method"
Kind string `json:"kind"` // func, method, type, const, var
Signature string `json:"signature"`
Doc string `json:"doc,omitempty"`
Def string `json:"def"` // file:line, bundle-relative
Refs []string `json:"refs,omitempty"` // file:line:col sites, sorted
Exported bool `json:"exported,omitempty"` // visible outside its package
}
Symbol is one package-scope symbol, or a method of a named or interface type. Unexported symbols are indexed too: a panic's frames are usually unexported, and that is exactly when an offline index earns its place.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package auto is the init()-time integration — the net/http/pprof of selfsource.
|
Package auto is the init()-time integration — the net/http/pprof of selfsource. |
|
Package browser serves a read-only HTTP source browser over a bundle — a pprof-style self-serve UI.
|
Package browser serves a read-only HTTP source browser over a bundle — a pprof-style self-serve UI. |
|
chat: an agent loop over the bundle — "I'm the binary, ask me anything".
|
chat: an agent loop over the bundle — "I'm the binary, ask me anything". |
|
cmd
|
|
|
selfsource
command
Command selfsource generates and inspects source bundles.
|
Command selfsource generates and inspects source bundles. |
|
Package gen generates a selfsource from a module tree at build time.
|
Package gen generates a selfsource from a module tree at build time. |
|
Package mcpserver exposes a bundle's query surface as MCP tools, for embedding in a host binary (an always-on `yourapp source mcp` verb) or via the selfsource CLI.
|
Package mcpserver exposes a bundle's query surface as MCP tools, for embedding in a host binary (an always-on `yourapp source mcp` verb) or via the selfsource CLI. |
|
Package profile decodes pprof profiles and joins them against an embedded selfsource bundle, so a binary that carries its own source can explain its own hot paths — no repo checkout, no `go tool pprof -source_path`.
|
Package profile decodes pprof profiles and joins them against an embedded selfsource bundle, so a binary that carries its own source can explain its own hot paths — no repo checkout, no `go tool pprof -source_path`. |
