Embedded shell for the Go runtime.
Status: Experimental
gosh is a runtime-aware shell embedded in a Go process. It exposes live Go runtime state (goroutines, stacks, memory, GC, metrics, profiles, traces) through a virtual filesystem and POSIX-like commands. It can run locally, be embedded in services, or be served over SSH.
Concept: /proc for goroutines inside your Go binary.
- Runtime-native introspection without external agents.
- /proc-like virtual filesystem for runtime state.
- POSIX pipelines and redirection via mvdan.cc/sh.
- Embeddable in any Go program.
- Extensible with custom structs and functions.
$ ssh 127.0.0.1 -p 2222
gosh> ls /proc
1
2
debug
goroutines
log
mem
metrics
pprof
stack
trace
# Summary of all goroutines
gosh> goroutines | head -n 5
ID STATE TOP
1 running runtime.gopark
2 waiting main.main
...
# Inspect a single goroutine
gosh> cat /proc/2/status
Goroutine: 2
State: waiting
Top: main.main
Frames: ...
# Capture a CPU profile (writes to virtual FS)
gosh> profile cpu 5 /cpu.pprof
- Go 1.25.5 or newer
- Unix-like systems recommended
go get github.com/sleepymole/gosh@latestpackage main
import (
"context"
"log"
"os"
"github.com/sleepymole/gosh"
)
func main() {
sh, err := gosh.New(gosh.Options{
In: os.Stdin,
Out: os.Stdout,
Err: os.Stderr,
Prompt: "gosh> ",
})
if err != nil {
log.Fatal(err)
}
if err := sh.Run(context.Background()); err != nil {
log.Fatal(err)
}
}sh, _ := gosh.New(gosh.Options{Out: os.Stdout, Err: os.Stderr})
_ = sh.Exec(context.Background(), "goroutines | head -n 5")ctx := context.Background()
err := gosh.ServeSSH(ctx, gosh.Options{}, gosh.SSHOptions{
Addr: "127.0.0.1:2222",
// Provide auth before binding to non-loopback addresses.
})- Virtual filesystem backed by memory and scoped to the Go process.
- /proc-like layout that mirrors runtime state.
- Text-first built-ins designed for pipelines.
/proc/<id>(dir): goroutine entry withstackandstatus./proc/<id>/stack: stack dump for a goroutine./proc/<id>/status: summary fields for a goroutine./proc/goroutines: tabular goroutine summary./proc/stack: full goroutine dump./proc/mem: memory and GC stats./proc/metrics: runtime metrics./proc/metrics/<prefix>: filtered metrics./proc/log/level: read/write log level string./proc/debug/memory: writefreeorgc(dangerous)./proc/pprof/heap: heap profile./proc/pprof/profile?seconds=10: CPU profile./proc/trace?seconds=5: runtime trace (dangerous)./proc/gc: trigger GC (dangerous).
help, version, clear, pwd, cd, ls, stat, cat, head, tail,
grep, wc, echo, printf, tee, touch, mkdir, rmdir, rm, cp,
mv, mount, umount, quit, exit.
goroutines (alias ps), stack, mem, metrics, profile, trace, gc,
call.
Scripts live in the virtual filesystem and are discovered via PATH (default
/bin). Files are executable if they have any exec bit or a shebang line.
#!/bin/goshor.goshextension (also accepts#!/usr/bin/env gosh).- Executed by the embedded POSIX interpreter.
#!/bin/gosh-goor.goextension (also accepts#!/usr/bin/env gosh-go).- Executed with Yaegi.
- Safe mode uses a restricted stdlib allowlist (see
yaegi_filter.go). EnableDangerousenables the full Yaegi stdlib, including OS and network.
Go scripts can import gosh to interact with the shell:
Args() []string: command arguments.Stdin(),Stdout(),Stderr(): streaming I/O.Cwd(): current working directory.ReadFile,WriteFile: access the virtual filesystem.Builtin(name, args...): invoke built-in commands.Call(name, payload): invoke registered Go functions (JSON in, JSON out).Fatal(err): stop the script with an error.
sh, _ := gosh.New(gosh.Options{
Funcs: map[string]gosh.Func{
"reload": func(ctx context.Context, input []byte) (any, error) {
return map[string]any{"ok": true}, nil
},
},
})Call it from the shell:
gosh> call reload {}
type Config struct {
Enabled bool
Name string `gosh:"name"`
}
func (c *Config) Reload() error {
return nil
}
sh, _ := gosh.New(gosh.Options{})
_ = sh.FS().Expose("/proc/app", &Config{})- Fields are readable and writable files.
- Nested structs become subdirectories.
- Methods become write-only files; writing triggers the call.
- StructFS does not provide locking; coordinate concurrent access in the host.
Mount a host directory into the virtual filesystem (dangerous mode):
mount -t host /var/log /mnt/log
mount -t host -o ro /etc /mnt/etc
umount /mnt/logEnableDangerousgates actions liketrace,gc, and host mounts.- Safe mode restricts Go scripts to a limited stdlib allowlist.
- SSH refuses non-loopback binds without authentication configured.
gosh favors a small, predictable subset of POSIX-style commands over full GNU
coreutils compatibility. Most commands implement only a handful of flags, and
unsupported options return an error. Use -- to pass arguments that begin with
- as positional paths or patterns.
Supported flags today:
ls:-l,--color=always|auto|never,--no-colorhead/tail:-n N
- Improve autocomplete and interactive UX.
- Expand runtime metrics and profiling surfaces.
- Add more built-in scripts for common diagnostics.
- Document and test Windows support explicitly.
Contributions are welcome. Please open an issue or submit a PR.
This project is licensed under the BSD-3-Clause License.
github.com/google/gops: CLI and agent for Go process diagnostics.net/http/pprof,runtime/pprof: profiling endpoints and data sources.runtime/trace,go tool trace: scheduler and trace events.github.com/arl/statsviz: browser UI for runtime metrics and profiles.github.com/go-delve/delve: full debugger for Go.github.com/traefik/yaegi,github.com/x-motemen/gore: Go interpreters.