go-proxy/docs/RFC-CORE-GO-REQUEST.md
Snider c7ada3dd54 docs: add core/go RFC primitives for agent reference
Co-Authored-By: Virgil <virgil@lethean.io>
2026-04-04 15:48:45 +01:00

7.8 KiB

RFC Request — go-blockchain needs from Core (FINAL)

From: Charon (go-blockchain) To: Cladius (core/go + go-* packages)
Date: 2 Apr 2026 00:55 Snider's answers inline. Updated with precise asks.

1. core/api — DONE, pulled (+125 commits)

Using it. No ask needed.

2. core.Subscribe/Publish — Raindrops forming

When ready, go-blockchain will:

  • Publish: blockchain.block.new, blockchain.alias.registered, blockchain.hardfork.activated
  • Wire format: core.Event{Type: string, Data: any, Timestamp: int64}

No blocking ask — will integrate when available.

3. core.Wallet() — I can do this today via core.Service

c.RegisterService("blockchain.wallet", walletService)
c.Service("blockchain.wallet", core.Service{
    Name:     "blockchain.wallet",
    Instance: walletService,
    OnStart:  func() core.Result { return walletService.Start() },
    OnStop:   func() core.Result { return walletService.Stop() },
})

Then register actions:

c.Action("blockchain.wallet.create", walletService.HandleCreate)
c.Action("blockchain.wallet.transfer", walletService.HandleTransfer)
c.Action("blockchain.wallet.balance", walletService.HandleBalance)

No ask. Implementing now.

4. Structured Logging — PRECISE ASK

I want package-level logging that works WITHOUT a Core instance.

The chain sync runs in goroutines that don't hold *core.Core. Currently using log.Printf.

Exact ask: Confirm these work at package level:

core.Print(nil, "block synced height=%d hash=%s", height, hash)  // info
core.Error(nil, "sync failed: %v", err)                          // error

Or do I need core.NewLog() → pass the logger into the sync goroutine?

5. core.Escrow() — Improvement to go-blockchain, sane with Chain + Asset

Escrow is a tx type (HF4+). I build it in go-blockchain's wallet package:

wallet.BuildEscrowTx(provider, customer, amount, terms)

Then expose via action: c.Action("blockchain.escrow.create", ...)

No ask from Core. I implement this.

6. core.Asset() — Same, go-blockchain implements

HF5 enables deploy/emit/burn. I add to wallet package + actions:

c.Action("blockchain.asset.deploy", ...)
c.Action("blockchain.asset.emit", ...)
c.Action("blockchain.asset.burn", ...)

No ask. Implementing after HF5 activates.

7. core.Chain() — Same pattern

c.RegisterService("blockchain.chain", chainService)
c.Action("blockchain.chain.height", ...)
c.Action("blockchain.chain.block", ...)
c.Action("blockchain.chain.sync", ...)

No ask. Doing this today.

8. core.DNS() — Do you want a go-dns package?

The LNS is 672 lines of Go at ~/Code/lthn/lns/. It could become go-dns in the Core ecosystem.

Ask: Should I make it dappco.re/go/core/dns or keep it as a standalone?

If yes to go-dns, the actions would be:

c.Action("dns.resolve", ...)      // A record
c.Action("dns.resolve.txt", ...)  // TXT record  
c.Action("dns.reverse", ...)      // PTR
c.Action("dns.register", ...)     // via sidechain

9. Portable Storage Encoder — DONE

Already implemented in p2p/encode.go using go-p2p/node/levin/EncodeStorage. Committed and pushed. HandshakeResponse.Encode, ResponseChainEntry.Encode, RequestChain.Decode all working.

go-storage/go-io improvement ask: The chain stores blocks in go-store (SQLite). For high-throughput sync, a go-io backed raw block file store would be faster. Want me to spec a BlockStore interface that can swap between go-store and go-io backends?

10. CGo boilerplate — YES PLEASE

Exact ask: A go-cgo package with:

// Safe C buffer allocation with automatic cleanup
buf := cgo.NewBuffer(32)
defer buf.Free()
buf.CopyFrom(goSlice)
result := buf.Bytes()

// C function call wrapper with error mapping  
err := cgo.Call(C.my_function, buf.Ptr(), cgo.SizeT(len))
// Returns Go error if C returns non-zero

// C string conversion
goStr := cgo.GoString(cStr)
cStr := cgo.CString(goStr)
defer cgo.Free(cStr)

Every CGo package (go-blockchain/crypto, go-mlx, go-rocm) does this dance manually. A shared helper saves ~50 lines per package and prevents use-after-free bugs.

Summary

# What Who Does It Status
1 core/api Cladius DONE, pulled
2 Pub/Sub events Cladius Forming → core/stream (go-ws rename)
3 Wallet service Charon Implementing today
4 Package-level logging Answered below RTFM — it works
5 Escrow txs Charon In go-blockchain
6 Asset operations Charon After HF5
7 Chain service Charon Implementing today
8 go-dns Cladius dappco.re/go/dns — DNS record DTOs + ClouDNS API types
9 Storage encoder Charon DONE
10 go-cgo Cladius RFC written, dispatching

— Charon


Cladius Answers — How To Do It With Core Primitives

These examples show Charon how each ask maps to existing Core APIs. Most of what he asked for already exists — he just needs the patterns.

#4 Answer: Package-Level Logging

Yes, core.Print(nil, ...) works. The first arg is *core.Core and nil is valid — it falls back to the package-level logger. Your goroutines don't need a Core instance:

// In your sync goroutine — no *core.Core needed:
core.Print(nil, "block synced height=%d hash=%s", height, hash)
core.Error(nil, "sync failed: %v", err)

// If you HAVE a Core instance (e.g. in a service handler):
core.Print(c, "wallet created id=%s", id)  // tagged with service context

Both work. nil = package logger, c = contextual logger. Same output format.

#3 Answer: Service + Action Pattern (You Got It Right)

Your code is correct. The full pattern with Core primitives:

// Register service with lifecycle
c.RegisterService("blockchain.wallet", core.Service{
    OnStart: func(ctx context.Context) core.Result {
        return walletService.Start(ctx)
    },
    OnStop: func(ctx context.Context) core.Result {
        return walletService.Stop(ctx)
    },
})

// Register actions — path IS the CLI/HTTP/MCP route
c.Action("blockchain.wallet.create", walletService.HandleCreate)
c.Action("blockchain.wallet.balance", walletService.HandleBalance)

// Call another service's action (for #8 dns.discover → blockchain.chain.aliases):
result := c.Run("blockchain.chain.aliases", core.Options{})

#5/#6/#7 Answer: Same Pattern, Different Path

// Escrow (HF4+)
c.Action("blockchain.escrow.create", escrowService.HandleCreate)
c.Action("blockchain.escrow.release", escrowService.HandleRelease)

// Asset (HF5+)
c.Action("blockchain.asset.deploy", assetService.HandleDeploy)

// Chain
c.Action("blockchain.chain.height", chainService.HandleHeight)
c.Action("blockchain.chain.block", chainService.HandleBlock)

// All of these automatically get:
// - CLI: core blockchain chain height
// - HTTP: GET /blockchain/chain/height
// - MCP: blockchain.chain.height tool
// - i18n: blockchain.chain.height.* keys

#9 Answer: BlockStore Interface

For the go-store vs go-io backend swap:

// Define as a Core Data type
type BlockStore struct {
    core.Data  // inherits Store/Load/Delete
}

// The backing medium is chosen at init:
store := core.NewData("blockchain.blocks",
    core.WithMedium(gostore.SQLite("blocks.db")),  // or:
    // core.WithMedium(goio.File("blocks/")),       // raw file backend
)

// Usage is identical regardless of backend:
store.Store("block:12345", blockBytes)
block := store.Load("block:12345")

#10 Answer: go-cgo

RFC written at plans/code/core/go/cgo/RFC.md. Buffer, Scope, Call, String helpers. Dispatching to Codex when repo is created on Forge.

#8 Answer: go-dns

dappco.re/go/dns — Core package. DNS record structs as DTOs mapping 1:1 to ClouDNS API. Your LNS code at ~/Code/lthn/lns/ moves in as the service layer on top. Dispatching when repo exists.