ast

package module
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 17, 2026 License: MIT Imports: 12 Imported by: 0

README

Language AST transform for blotless Layer B
Go + Python built-in · WASM plugins (wazero capability ABI) · Zero CGO

blotless/ast

Semantic-preserving transforms that disrupt token-sampling watermarks
(Kirchenbauer / SynthID-Text class). Not a vendor-key verifier.

Language: English | Русский

CI Go Reference Go Report Card License Latest Release Go Version


Overview

ast is the language-transform library of the blotless ecosystem. blotless/engine calls ast.Transform during clean --layer-b. blotless/cli loads extra languages with --ast-wasm.

It does not certify “human-written” text and does not reverse SynthID without a vendor key.

Key Features

Category Capabilities
Built-in Go Rename unexported locals/params; reorder private struct fields; sort imports; gofmt
Built-in Python Rename private locals; sort imports; conservative class-body assign reorder
WASM plugins Any language compiled to wasm32-unknown-unknown; capability ABI only
Sandbox wazero, zero CGO; no guest FS / network / env
Registry Register (in-process) · RegisterWASM (local .wasm) · MapExt
Protect Go string literals / go:generate stay out of ident rewrites in the Go driver

Installation

go get github.com/blotless/ast

Requirements:

  • Go 1.26+
  • CGO_ENABLED=0
CGO_ENABLED=0 go test ./...

Quick Start

package main

import (
	"fmt"

	"github.com/blotless/ast"
)

func main() {
	src := []byte("package p\n\nfunc F() {\n\tx := 1\n\t_ = x\n}\n")
	out, rep, err := ast.Transform(ast.LangGo, src, ast.DefaultOpts())
	if err != nil {
		panic(err)
	}
	fmt.Printf("changed=%v renamed=%d\n%s\n", rep.Changed, rep.Renamed, out)
}

In-process drivers implement ast.Driver (Lang + Transform) and are registered with ast.Register. Built-in Go and Python register in init.


Built-in languages

Lang Extensions Driver
go .go golang/
python .py python/

Defaults (ast.DefaultOpts): rename locals, reorder fields, reorder imports, format, seed=1.


Add a language via WASM (no Go required)

Write a module in any language that compiles to WebAssembly, implement transform, import only blotless_* host functions. Full copy-paste crate: examples/wasm-rust/.

Module shape

my-transform/
├── Cargo.toml          # crate-type = ["cdylib"]
└── src/lib.rs          # export transform(); import blotless::*

Guest exports:

Export Type Meaning
memory memory Linear memory
transform () -> i32 0 = ok; non-zero = fail

Guest imports (module name blotless) — full table in ABI.md:

abi_version, get_opts, src_len, src_read, src_write, report, log, fail.

Minimal Rust skeleton:

#[link(wasm_import_module = "blotless")]
extern "C" {
    fn src_len() -> u32;
    fn src_read(dest: u32, off: u32, n: u32) -> u32;
    fn src_write(ptr: u32, n: u32) -> u32;
    fn report(ptr: u32, n: u32) -> u32;
}

#[no_mangle]
pub extern "C" fn transform() -> i32 {
    // src_read → transform → src_write → report
    0
}

Build

rustup target add wasm32-unknown-unknown
cargo build --release --target wasm32-unknown-unknown
# → target/wasm32-unknown-unknown/release/blotless_rust_transform.wasm

Do not use WASI (wasm32-wasip1). The host does not provide FS or sockets.

Install / connect

--ast-wasm rust=path.wasm loads the plugin and maps .rs automatically.

CGO_ENABLED=0 go install github.com/blotless/cli/cmd/blotless@latest

blotless clean ./src --write --layer-b \
  --ast-wasm rust=./blotless_rust_transform.wasm

Custom language id (example: Zig):

blotless clean . --write --layer-b \
  --ast-wasm zig=/opt/blotless/zig_transform.wasm \
  --ast-ext .zig=zig
Flag Example Meaning
--ast-wasm rust=./rust_transform.wasm Local .wasm for language id
--ast-ext .zig=zig Extra extension map (optional; .rs is automatic for rust)

Engine:

eng, err := engine.New(engine.Config{
    Paths:   []string{"."},
    LayerB:  true,
    AstWASM: map[string]string{"rust": "./blotless_rust_transform.wasm"},
})

Library:

ast.RegisterWASM("rust", "./blotless_rust_transform.wasm")
out, rep, err := ast.Transform("rust", src, ast.DefaultOpts())

Rules:

  • Path must be a local file (URLs rejected)
  • Built-in Go/Python win for .go / .py
  • On fail / parse error, blotless keeps original bytes for that file
  • Timeout ~5s, bounded guest memory

Checklist

  • Parse failure → fail + non-zero; host keeps original
  • Rename only non-public locals
  • Stable renames when seed is set
  • Fill report (lang, renamed, reordered, changed)
  • No files / sockets / env (imports are not provided)

Add an in-tree Go driver

  1. Package ast/<lang>/ with Transform(src []byte, opts Opts) ([]byte, Result, error) — do not import root package ast (import cycle).
  2. Thin adapter in builtin.go.
  3. Tests + fixtures.

Security

  • WASM paths: local files only
  • No raw OS-exec plugins
  • Guest cannot open files, dial network, or read env
  • See SECURITY.md

Architecture

See docs/ARCHITECTURE.md. Host runtime: wasm_host.go (wazero).

cli --ast-wasm rust=./x.wasm
        │
        ▼
engine.Config.AstWASM  →  ast.RegisterWASM
        │
        ▼
clean --layer-b  →  ast.Transform("rust", src, opts)
        │
        ▼
wazero guest: export transform() · import blotless.*

Ecosystem

Project Role
blotless/ast This repo
blotless/engine Calls ast.Transform on clean
blotless/cli --layer-b, --ast-wasm, --ast-ext
blotless/skills Agent skill pack

Development

git clone https://github.com/blotless/ast
cd ast
CGO_ENABLED=0 go test ./...

Contributor docs: CONTRIBUTING.md · CODE_OF_CONDUCT.md · ROADMAP.md


Disclaimer

Transforms are best-effort token-sequence disruption. They cannot certify that a vendor detector will fail.


License

MIT License — see LICENSE.


blotless — inspect first, then clean

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func DefaultExts

func DefaultExts(lang Lang) []string

DefaultExts returns conventional file extensions for a language id.

func HasDriver

func HasDriver(lang Lang) bool

HasDriver reports whether lang has a built-in or WASM driver.

func MapExt

func MapExt(ext string, lang Lang)

MapExt associates a file extension (with dot) to a language id.

func Register

func Register(d Driver)

Register adds an in-process driver (built-in languages).

func RegisterWASM

func RegisterWASM(lang Lang, path string) error

RegisterWASM binds lang to a local .wasm plugin path (capability-sandboxed).

Types

type Driver

type Driver interface {
	Lang() Lang
	Transform(src []byte, opts Opts) ([]byte, Report, error)
}

Driver is an in-process language transform implementation.

type Lang

type Lang string

Lang identifies a source language for transform.

const (
	LangGo     Lang = "go"
	LangPython Lang = "python"
)

func LangFromPath

func LangFromPath(path string) (Lang, bool)

LangFromPath returns the language for a file path, if mapped.

type Opts

type Opts struct {
	RenameLocals   bool  `json:"rename_locals"`
	ReorderFields  bool  `json:"reorder_fields"`
	ReorderImports bool  `json:"reorder_imports"`
	Format         bool  `json:"format"`
	Seed           int64 `json:"seed"`
}

Opts configures semantic-preserving AST transform.

func DefaultOpts

func DefaultOpts() Opts

DefaultOpts returns conservative transform defaults.

type Report

type Report struct {
	Lang      Lang `json:"lang"`
	Renamed   int  `json:"renamed"`
	Reordered int  `json:"reordered"`
	Changed   bool `json:"changed"`
}

Report summarizes what a driver changed.

func Transform

func Transform(lang Lang, src []byte, opts Opts) ([]byte, Report, error)

Transform runs the language driver (built-in preferred over WASM).

Directories

Path Synopsis
Package astgo parses Go source and yields byte-offset spans for comments, identifiers, and string literals.
Package astgo parses Go source and yields byte-offset spans for comments, identifiers, and string literals.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL