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,56 @@
|
||||
canonical_id: "platform_crypto"
|
||||
display_name: "Platform Crypto Primitive"
|
||||
category: "platform"
|
||||
version: "v1"
|
||||
wasi_target: "preview1"
|
||||
stability: "stable"
|
||||
runtime_compat:
|
||||
- "cf-workers"
|
||||
- "workerd"
|
||||
- "wazero"
|
||||
constraints:
|
||||
max_size_kb: 2048
|
||||
max_cold_start_ms: 50
|
||||
no_network_syscall: true
|
||||
no_filesystem_syscall: true
|
||||
io_model: "stdin_stdout_json"
|
||||
input_schema:
|
||||
type: object
|
||||
required: [action]
|
||||
properties:
|
||||
action:
|
||||
type: string
|
||||
enum: [generate_api_key, encrypt, random_token]
|
||||
email:
|
||||
type: string
|
||||
description: generate_api_key 用
|
||||
plaintext:
|
||||
type: string
|
||||
description: encrypt 用
|
||||
bytes:
|
||||
type: integer
|
||||
description: random_token 用,預設 32
|
||||
output_schema:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
api_key:
|
||||
type: string
|
||||
description: generate_api_key 結果,ak_ 前綴
|
||||
encrypted:
|
||||
type: string
|
||||
description: encrypt 結果,base64
|
||||
iv:
|
||||
type: string
|
||||
description: encrypt 結果,base64
|
||||
token:
|
||||
type: string
|
||||
description: random_token 結果,hex
|
||||
tags: [platform, crypto, internal]
|
||||
description: |
|
||||
平台內部 crypto primitive。
|
||||
- generate_api_key: HMAC-SHA256(email, ENCRYPTION_KEY) → ak_xxx
|
||||
- encrypt: AES-GCM(plaintext, ENCRYPTION_KEY) → {encrypted, iv}(base64)
|
||||
- random_token: crypto random bytes → hex string
|
||||
ENCRYPTION_KEY 由 host 持有,永不進入 WASM。
|
||||
@@ -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