Open source · MIT licensed

An embeddable Ruby-like language for Go. Safe by default, easy for AI to write.

Vibescript is a small Ruby-like language that runs inside your Go app. Users and AI agents write scripts to add features. Your app decides exactly what each script can touch.

206 examples run live in your browser

late_fee.vibe v1.0.0-rc9
def late_fee(overdue: duration) -> money
  if overdue <= 3.days
    money("0.00 USD")
  elsif overdue <= 14.days
    money("5.00 USD")
  else
    money("15.00 USD")
  end
end

def run
  {
    on_time: late_fee(2.days),
    grace: late_fee(10.days),
    overdue: late_fee(3.weeks)
  }
end

      
    

Language

A familiar language for app logic.

Ruby-like syntax, optional types, and built-in money, duration, and enum values. Everything is designed to be read at a glance, by your team and by the models writing it.

Ruby-like syntax with optional types.

Blocks, ranges, calls without parentheses, and symbol-keyed hashes will look familiar to Ruby users. Types are optional. When you use them, Vibescript checks arguments and return values. Nullable types stay explicit.

typing.vibe
def shipping_quote(speed, subtotal: money) -> hash
  fee = shipping_fee(speed, subtotal)
  transit = transit_time(speed)

  {
    speed: speed.name,
    fee: fee,
    total: subtotal + fee,
    transit: transit.iso8601
  }
end

Embed it with three calls.

One go get, then three calls: create an engine, compile the script, call a function. The script exposes functions to your app. Your app exposes only the capabilities it chooses to share back.

main.go host side
engine, _ := vibes.NewEngine(vibes.Config{
  StepQuota: 250_000,
})

script, _ := engine.Compile(userScript)

fee, _ := script.Call(ctx, "late_fee", args, opts)
fee.String() // "5.00 USD"

Set hard limits for each script.

A runaway loop, a hostile script, an AI hallucination with a while-true in it. The engine stops each one at the step, memory, or recursion limit and returns a clean error to your Go app. No goroutine leak, no OOM, no incident channel.

untrusted.vibe · sandboxed
$ cat untrusted.vibe
def run
  balance = 0
  while true
    balance = balance + 1
  end
end

$ # host calls script.Call(ctx, "run", ...)
RuntimeError: step quota exceeded
✓ stopped at 250k steps, Go app keeps running

Built-in money, duration, and enum types.

3.days is a duration, not an integer with a comment. Money uses exact decimal values, so float rounding cannot lose a cent. Enums are real values, not strings you promise to spell consistently.

primitives.vibe
enum ShippingSpeed
  Economy
  Priority
  Overnight
end

def transit_time(speed: ShippingSpeed) -> duration
  if speed == ShippingSpeed::Overnight
    6.hours
  else
    5.days
  end
end

def net_after_fee(cents)
  gross = money_cents(cents, "USD")
  gross - money("1.75 USD")
end

Extension model

Your Go app stays in charge.

Lua is often embedded in games and tools so people can extend them with scripts. Vibescript brings that model to Go apps.

Each Vibescript extension works through the functions, data, and services your app exposes. The rest of the host stays out of reach.

Extending an app this way feels more like HyperCard than traditional software development. Flexible, but within bounds. Scripts get an opinionated set of primitives that combine into predictable features, not a blank slate that can take your app down.

Try it

Run a few examples.

206 programs are compiled against the real interpreter when the site starts and run with one click. No install or setup needed.