arcrun — AI workflow execution engine (clean history)
Self-hosted 開源:WASM 零件 + recipe + cypher-executor,跑在你自己的 Cloudflare。 此為重建的乾淨歷史起點(移除曾誤 commit 的 GCP SA 金鑰,舊歷史保留在 richblack/arcrun 與本地 backup 分支)。含: - acr init --self-hosted installer(建 KV/R2 + codeload 拉預編譯 wasm + wrangler deploy + seed recipe) - recipe push 把關(資料外流提醒 + 打通檢查) - 19 個正當零件預編譯 wasm(claude_api/km_writer/kbdb_upsert_block 排除:違反 DECISIONS §1) - CLI / cypher-executor / registry / 完整 SDD Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,206 @@
|
||||
// platform_crypto — Arcrun 平台內部 crypto primitive
|
||||
//
|
||||
// Actions:
|
||||
// generate_api_key — HMAC-SHA256(email, ENCRYPTION_KEY) → ak_{hex[:32]}
|
||||
// encrypt — AES-GCM(plaintext, ENCRYPTION_KEY) → {encrypted, iv}(base64)
|
||||
// random_token — crypto random bytes → hex string
|
||||
//
|
||||
// ENCRYPTION_KEY 由 host 持有,永不進入 WASM。
|
||||
//
|
||||
// Host imports:
|
||||
// u6u.crypto_hmac_sha256 — HMAC-SHA256(data, key=ENCRYPTION_KEY) → raw bytes
|
||||
// u6u.crypto_aes_encrypt — AES-GCM(plaintext, key=ENCRYPTION_KEY) → encrypted_b64 + iv_b64
|
||||
// u6u.crypto_random_bytes — crypto-random bytes → hex string
|
||||
//
|
||||
//go:build tinygo
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// ── host function 宣告 ───────────────────────────────────────────────────────
|
||||
|
||||
// crypto_hmac_sha256(dataPtr, dataLen, outPtr, outLenPtr) → 0 成功
|
||||
// key = host 的 ENCRYPTION_KEY,output = raw bytes(hex encode 由 WASM 做)
|
||||
//
|
||||
//go:wasmimport u6u crypto_hmac_sha256
|
||||
func hostCryptoHmacSha256(
|
||||
dataPtr uintptr, dataLen uint32,
|
||||
outPtr uintptr, outLenPtr uintptr,
|
||||
) uint32
|
||||
|
||||
// crypto_aes_encrypt(plaintextPtr, plaintextLen, outEncPtr, outEncLenPtr, outIvPtr, outIvLenPtr) → 0 成功
|
||||
// output: encrypted(base64)放 outEnc,iv(base64)放 outIv
|
||||
//
|
||||
//go:wasmimport u6u crypto_aes_encrypt
|
||||
func hostCryptoAesEncrypt(
|
||||
plaintextPtr uintptr, plaintextLen uint32,
|
||||
outEncPtr uintptr, outEncLenPtr uintptr,
|
||||
outIvPtr uintptr, outIvLenPtr uintptr,
|
||||
) uint32
|
||||
|
||||
// crypto_random_bytes(numBytes, outPtr, outLenPtr) → 0 成功
|
||||
// output: hex string
|
||||
//
|
||||
//go:wasmimport u6u crypto_random_bytes
|
||||
func hostCryptoRandomBytes(
|
||||
numBytes uint32,
|
||||
outPtr uintptr, outLenPtr uintptr,
|
||||
) uint32
|
||||
|
||||
// ── 型別 ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
type Input struct {
|
||||
Action string `json:"action"`
|
||||
Email string `json:"email,omitempty"`
|
||||
Plaintext string `json:"plaintext,omitempty"`
|
||||
Bytes int `json:"bytes,omitempty"`
|
||||
}
|
||||
|
||||
// ── main ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
func main() {
|
||||
raw, err := io.ReadAll(os.Stdin)
|
||||
if err != nil {
|
||||
writeError("failed to read stdin: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
var input Input
|
||||
if err := json.Unmarshal(raw, &input); err != nil {
|
||||
writeError("invalid input JSON: " + err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
switch input.Action {
|
||||
case "generate_api_key":
|
||||
if input.Email == "" {
|
||||
writeError("email 必填")
|
||||
return
|
||||
}
|
||||
sig, ok := hmacSha256([]byte(input.Email))
|
||||
if !ok {
|
||||
writeError("HMAC-SHA256 失敗")
|
||||
return
|
||||
}
|
||||
apiKey := "ak_" + hex(sig)[:32]
|
||||
out, _ := json.Marshal(map[string]interface{}{
|
||||
"success": true,
|
||||
"api_key": apiKey,
|
||||
})
|
||||
os.Stdout.Write(out)
|
||||
|
||||
case "encrypt":
|
||||
if input.Plaintext == "" {
|
||||
writeError("plaintext 必填")
|
||||
return
|
||||
}
|
||||
encB64, ivB64, ok := aesEncrypt([]byte(input.Plaintext))
|
||||
if !ok {
|
||||
writeError("AES-GCM 加密失敗")
|
||||
return
|
||||
}
|
||||
out, _ := json.Marshal(map[string]interface{}{
|
||||
"success": true,
|
||||
"encrypted": encB64,
|
||||
"iv": ivB64,
|
||||
})
|
||||
os.Stdout.Write(out)
|
||||
|
||||
case "random_token":
|
||||
n := input.Bytes
|
||||
if n <= 0 {
|
||||
n = 32
|
||||
}
|
||||
token, ok := randomBytes(uint32(n))
|
||||
if !ok {
|
||||
writeError("random bytes 失敗")
|
||||
return
|
||||
}
|
||||
out, _ := json.Marshal(map[string]interface{}{
|
||||
"success": true,
|
||||
"token": token,
|
||||
})
|
||||
os.Stdout.Write(out)
|
||||
|
||||
default:
|
||||
writeError("不支援的 action: " + input.Action)
|
||||
}
|
||||
}
|
||||
|
||||
// ── helpers ───────────────────────────────────────────────────────────────────
|
||||
|
||||
func writeError(msg string) {
|
||||
out, _ := json.Marshal(map[string]interface{}{
|
||||
"success": false,
|
||||
"error": msg,
|
||||
})
|
||||
os.Stdout.Write(out)
|
||||
}
|
||||
|
||||
func hmacSha256(data []byte) ([]byte, bool) {
|
||||
if len(data) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
outBuf := make([]byte, 64) // SHA-256 = 32 bytes raw
|
||||
var outLen uint32
|
||||
status := hostCryptoHmacSha256(
|
||||
uintptr(unsafe.Pointer(&data[0])), uint32(len(data)),
|
||||
uintptr(unsafe.Pointer(&outBuf[0])), uintptr(unsafe.Pointer(&outLen)),
|
||||
)
|
||||
if status != 0 {
|
||||
return nil, false
|
||||
}
|
||||
return outBuf[:outLen], true
|
||||
}
|
||||
|
||||
func aesEncrypt(plaintext []byte) (string, string, bool) {
|
||||
if len(plaintext) == 0 {
|
||||
return "", "", false
|
||||
}
|
||||
encBuf := make([]byte, 65536)
|
||||
ivBuf := make([]byte, 64)
|
||||
var encLen, ivLen uint32
|
||||
status := hostCryptoAesEncrypt(
|
||||
uintptr(unsafe.Pointer(&plaintext[0])), uint32(len(plaintext)),
|
||||
uintptr(unsafe.Pointer(&encBuf[0])), uintptr(unsafe.Pointer(&encLen)),
|
||||
uintptr(unsafe.Pointer(&ivBuf[0])), uintptr(unsafe.Pointer(&ivLen)),
|
||||
)
|
||||
if status != 0 {
|
||||
return "", "", false
|
||||
}
|
||||
return string(encBuf[:encLen]), string(ivBuf[:ivLen]), true
|
||||
}
|
||||
|
||||
func randomBytes(n uint32) (string, bool) {
|
||||
outBuf := make([]byte, n*2+4) // hex = 2 chars per byte
|
||||
var outLen uint32
|
||||
status := hostCryptoRandomBytes(
|
||||
n,
|
||||
uintptr(unsafe.Pointer(&outBuf[0])), uintptr(unsafe.Pointer(&outLen)),
|
||||
)
|
||||
if status != 0 {
|
||||
return "", false
|
||||
}
|
||||
return string(outBuf[:outLen]), true
|
||||
}
|
||||
|
||||
// hex encodes raw bytes to lowercase hex string
|
||||
func hex(b []byte) string {
|
||||
const hexChars = "0123456789abcdef"
|
||||
out := make([]byte, len(b)*2)
|
||||
for i, v := range b {
|
||||
out[i*2] = hexChars[v>>4]
|
||||
out[i*2+1] = hexChars[v&0xf]
|
||||
}
|
||||
return string(out)
|
||||
}
|
||||
|
||||
// strings import 只為了 strings.Builder(interpolate 用,這裡不需要但 import 要保留給未來)
|
||||
var _ = strings.Builder{}
|
||||
Reference in New Issue
Block a user