Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ build-e2b-provider:

# The pinned SDK environment is also tested when building the shipped helper.
check-e2b-provider:
go run ./services/core/internal/sandbox/e2b/internal/contractgen --check
PYTHONDONTWRITEBYTECODE=1 $${OAC_TEST_E2B_SDK_PYTHON:-python3} -m unittest discover -s services/core/deploy/e2b -p '*_test.py'
PYTHONDONTWRITEBYTECODE=1 $${OAC_TEST_E2B_SDK_PYTHON:-python3} -m unittest discover -s services/core/tools/e2b-provider -p '*_test.py'

Expand Down
5 changes: 3 additions & 2 deletions services/core/deploy/e2b/build-template.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
for entry in tree.iterdir():
archive.add(entry, arcname=entry.name)
(context / 'runtime-env.json').write_text(json.dumps(environment))
for name in ['init.py', 'managed_init.py']:
for name in ['init.py', 'managed_init.py', 'helper_contract_generated.py']:
(context / name).write_bytes(Path(__file__).with_name(name).read_bytes())
template = (Template(file_context_path=context).from_image(BASE)
.run_cmd('apt-get update && apt-get install -y --no-install-recommends '
Expand All @@ -74,13 +74,14 @@
.copy('runtime-env.json', '/etc/oac-runtime-env.json', user='root')
.copy('init.py', '/opt/oac-e2b/init.py', user='root')
.copy('managed_init.py', '/opt/oac-e2b/managed_init.py', user='root')
.copy('helper_contract_generated.py', '/opt/oac-e2b/helper_contract_generated.py', user='root')
.run_cmd('tar --no-same-owner -xzf /root/runtime.tar.gz -C / && rm /root/runtime.tar.gz '
'&& usermod -l runtime -d /home/runtime node '
'&& mkdir -p /home/runtime/.oac /environment/workspace /environment/staging /environment/initialization /environment/packages /workspace '
'&& chown -R 1000:1000 /home/runtime /environment '
'&& chmod 0700 /home/runtime/.oac /environment/staging '
'&& chmod 0444 /etc/oac-runtime-env.json '
'&& chmod 0555 /opt/oac-e2b /opt/oac-e2b/init.py /opt/oac-e2b/managed_init.py', user='root')
'&& chmod 0555 /opt/oac-e2b /opt/oac-e2b/init.py /opt/oac-e2b/managed_init.py /opt/oac-e2b/helper_contract_generated.py', user='root')
.set_user('runtime').set_workdir('/environment/workspace'))
result = Template.build(template, name=args.name, cpu_count=2, memory_mb=2048,
on_build_logs=lambda entry: print(entry.message, flush=True),
Expand Down
6 changes: 6 additions & 0 deletions services/core/deploy/e2b/build_template_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ def build(instance, **kwargs):
self.assertEqual(modes[prefix + path], mode)
self.assertEqual(stat.S_IMODE((context / 'runtime.tar.gz').stat().st_mode), 0o666 & ~mask)
self.assertEqual(stat.S_IMODE(key.stat().st_mode), 0o600)
projection = 'helper_contract_generated.py'
self.assertEqual((context / projection).read_bytes(), Path(__file__).with_name(projection).read_bytes())
for source in ('init.py', 'managed_init.py', projection):
template.copy.assert_any_call(source, '/opt/oac-e2b/' + source, user='root')
self.assertTrue(any('chmod 0555' in call.args[0] and '/opt/oac-e2b/' + projection in call.args[0]
for call in template.run_cmd.call_args_list))
return SimpleNamespace(template_id='fixture', build_id='build')

factory = Mock(return_value=template)
Expand Down
18 changes: 18 additions & 0 deletions services/core/deploy/e2b/helper_contract_generated.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Code generated by contractgen; DO NOT EDIT.
"""Adapter-private wire declarations. No SDK or repository dependency."""
ERROR_CODES = ["","invalid","ownership","exists","not_found","command_unconfirmed","unconfirmed","template_invalid","team_mismatch","unauthorized"]
MANAGED_BOOTSTRAP_FIELDS = ["TenantID","EnvironmentID","AllocationID","SessionID","DeviceID","NetworkAccess","AllowedDomains","InstallationID","RuntimeBootstrap"]
MANAGED_IDENTITY_FIELDS = ["TenantID","EnvironmentID","AllocationID","SessionID","DeviceID","InstallationID"]
MAX_COMMAND_INPUT = 52428832
MAX_CREDENTIAL_REFERENCES = 32
MAX_OBSERVATION_REFERENCES = 100
MAX_OUTPUT = 1048576
MAX_REQUEST = 75497472
MAX_RESPONSE = 16777216
NETWORK_ACCESS = ["enabled","disabled","restricted"]
OPERATIONS = ["create","inspect","renew","kill","command","validate_deployment","observe","list_templates","list_builds","verify_credential"]
PROTOCOL_VERSION = 1
REFERENCE_FIELDS = ["TenantID","EnvironmentID","AllocationID"]
REQUEST_FIELDS = ["Version","Operation","Config","Reference","References","Bootstrap","RuntimeBootstrap","Command","Deadline"]
RESPONSE_FIELDS = ["Version","Info","Command","ErrorCode","DeploymentValid","TemplateBuild","Templates","Builds","Observations"]
SDK_VERSION = "2.51.0"
11 changes: 8 additions & 3 deletions services/core/deploy/e2b/managed_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,20 @@
_spec.loader.exec_module(shared)


_contract_spec = importlib.util.spec_from_file_location('helper_contract', Path(__file__).with_name('helper_contract_generated.py'))
contract = importlib.util.module_from_spec(_contract_spec)
_contract_spec.loader.exec_module(contract)


def identity(payload):
fields = ['InstallationID', 'TenantID', 'EnvironmentID', 'AllocationID', 'SessionID', 'DeviceID']
if not isinstance(payload, dict) or set(payload) != set(fields + ['NetworkAccess', 'AllowedDomains', 'RuntimeBootstrap']):
fields = contract.MANAGED_IDENTITY_FIELDS
if not isinstance(payload, dict) or set(payload) != set(contract.MANAGED_BOOTSTRAP_FIELDS):
raise ValueError('Invalid managed bootstrap fields')
for field in fields:
value = payload[field]
if not isinstance(value, str) or str(UUID(value)) != value or UUID(value).int == 0:
raise ValueError('Invalid managed bootstrap identity')
if (payload['NetworkAccess'] not in ('enabled', 'disabled', 'restricted') or
if (payload['NetworkAccess'] not in contract.NETWORK_ACCESS or
payload['AllowedDomains'] is not None and
(not isinstance(payload['AllowedDomains'], list) or
any(not isinstance(domain, str) for domain in payload['AllowedDomains']))):
Expand Down
25 changes: 25 additions & 0 deletions services/core/deploy/e2b/managed_init_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
import json
from pathlib import Path
import tempfile
import shutil
import subprocess
import sys
import unittest
from unittest.mock import Mock, patch
from uuid import uuid4
Expand All @@ -19,6 +22,28 @@ def payload():


class ManagedStartupTest(unittest.TestCase):
def test_isolated_packaged_import(self):
with tempfile.TemporaryDirectory() as temporary:
for name in ('init.py', 'managed_init.py', 'helper_contract_generated.py'):
shutil.copy2(Path(__file__).with_name(name), Path(temporary, name))
subprocess.run([sys.executable, '-I', '-c',
"import runpy,sys; runpy.run_path(sys.argv[1], run_name='fixture')",
str(Path(temporary, 'managed_init.py'))], cwd=temporary, check=True,
capture_output=True)

def test_shared_bootstrap_exchanges(self):
fixture = Path(__file__).resolve().parents[2] / 'tools/e2b-provider/testdata/contract.json'
for case in json.loads(fixture.read_text()):
if case['kind'] != 'managed':
continue
with self.subTest(name=case['name']):
try:
managed_init.identity(case['payload'])
valid = True
except (ValueError, TypeError, KeyError):
valid = False
self.assertEqual(valid, case['valid'])

def test_invalid_binding_rejected(self):
source = payload()
for key, value in [('DeviceID', 'other'), ('NetworkAccess', 'unknown')]:
Expand Down
2 changes: 1 addition & 1 deletion services/core/internal/sandbox/e2b/credential.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
func (p *Provider) VerifyCredential(ctx context.Context, refs []sandbox.Reference) error {
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
if len(refs) > 32 {
if len(refs) > MaxCredentialReferences {
return sandbox.ErrInvalid
}
for _, r := range refs {
Expand Down
111 changes: 111 additions & 0 deletions services/core/internal/sandbox/e2b/helper_contract.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package e2b

import (
"slices"
"time"

"github.com/MiniMax-AI/OpenAgentCore/internal/runtimebootstrap"
"github.com/MiniMax-AI/OpenAgentCore/services/core/internal/runtimeobs"
"github.com/MiniMax-AI/OpenAgentCore/services/core/internal/sandbox"
)

//go:generate go run ./internal/contractgen

// This adapter-private boundary is documented in tools/e2b-provider/README.md.
const ProtocolVersion = 1
const MaxOutputBytes = 1024 * 1024
const MaxRequestBytes = 72 * 1024 * 1024
const MaxResponseBytes = 16 * 1024 * 1024
const MaxCredentialReferences = 32
const MaxObservationReferences = runtimeobs.MaxBatchTargets
const MaxCommandInputBytes = sandbox.MaxCommandInputBytes

// HelperOperations declares the complete set of one-shot helper operations.
func HelperOperations() []string {
return []string{"create", "inspect", "renew", "kill", "command", "validate_deployment", "observe", "list_templates", "list_builds", "verify_credential"}
}

// HelperErrors are sanitized wire outcomes; an empty code denotes success.
func HelperErrors() []string {
return []string{"", "invalid", "ownership", "exists", "not_found", "command_unconfirmed", "unconfirmed", "template_invalid", "team_mismatch", "unauthorized"}
}

// Validate checks the operation envelope before the helper can start. Native
// configuration and operation outcomes retain their existing adapter validation.
func (q Request) Validate() error {
if q.Version != ProtocolVersion || !slices.Contains(HelperOperations(), q.Operation) || q.Deadline.IsZero() {
return sandbox.ErrInvalid
}
if q.Operation != "list_templates" && q.Operation != "list_builds" && !validID(q.Config.InstallationID) {
return sandbox.ErrInvalid
}
switch q.Operation {
case "observe":
if len(q.References) < 1 || len(q.References) > MaxObservationReferences {
return sandbox.ErrInvalid
}
seen := map[sandbox.Reference]bool{}
for _, r := range q.References {
if !validReference(r) || seen[r] {
return sandbox.ErrInvalid
}
seen[r] = true
}
case "verify_credential":
if len(q.References) > MaxCredentialReferences {
return sandbox.ErrInvalid
}
for _, r := range q.References {
if !validReference(r) {
return sandbox.ErrInvalid
}
}
case "validate_deployment", "list_templates", "list_builds":
default:
if !validReference(q.Reference) {
return sandbox.ErrInvalid
}
}
return nil
}

type Request struct {
Version int
Operation string
Config Config
Reference sandbox.Reference
// References lists the allocations of one read-only observe request.
References []sandbox.Reference `json:",omitempty"`
Bootstrap *sandbox.Bootstrap `json:",omitempty"`
RuntimeBootstrap *runtimebootstrap.Connection `json:",omitempty"`
Command *sandbox.Command `json:",omitempty"`
Deadline time.Time
}
type Response struct {
Version int
Info *sandbox.Info `json:",omitempty"`
Command *sandbox.CommandResult `json:",omitempty"`
ErrorCode string
DeploymentValid bool `json:",omitempty"`
TemplateBuild *TemplateBuild `json:",omitempty"`
Templates []TemplateSummary `json:",omitempty"`
Builds []ReadyBuild `json:",omitempty"`
Observations []Observation `json:",omitempty"`
}

func (r Response) Validate() error {
if r.Version != ProtocolVersion || !slices.Contains(HelperErrors(), r.ErrorCode) {
return sandbox.ErrInvalid
}
return nil
}

type TemplateSummary struct {
ID string `json:"id"`
Names []string `json:"names"`
}
type ReadyBuild struct {
ID string `json:"id"`
CPUs uint32 `json:"cpus"`
MemoryMiB uint32 `json:"memory_mib"`
}
92 changes: 92 additions & 0 deletions services/core/internal/sandbox/e2b/helper_contract_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package e2b

import (
"bytes"
"encoding/json"
"os"
"os/exec"
"testing"

"github.com/MiniMax-AI/OpenAgentCore/internal/agentnetwork"
"github.com/MiniMax-AI/OpenAgentCore/services/core/internal/sandbox"
)

func TestHelperProjectionFreshness(t *testing.T) {
command := exec.Command("go", "run", "./internal/contractgen", "--check")
if output, err := command.CombinedOutput(); err != nil {
t.Fatalf("%v: %s", err, output)
}
}

func TestSharedHelperExchanges(t *testing.T) {
data, err := os.ReadFile("../../../tools/e2b-provider/testdata/contract.json")
if err != nil {
t.Fatal(err)
}
var fixtures []struct {
Kind, Name string
Valid bool
Payload json.RawMessage
}
if err := json.Unmarshal(data, &fixtures); err != nil {
t.Fatal(err)
}
for _, fixture := range fixtures {
t.Run(fixture.Kind+"/"+fixture.Name, func(t *testing.T) {
decoder := json.NewDecoder(bytes.NewReader(fixture.Payload))
decoder.DisallowUnknownFields()
valid := false
switch fixture.Kind {
case "request":
var request Request
valid = decoder.Decode(&request) == nil && request.Validate() == nil
case "response":
var response Response
valid = decoder.Decode(&response) == nil && response.Validate() == nil
case "managed":
valid = validManagedExchange(fixture.Payload)
default:
t.Fatal("unknown fixture kind")
}
if valid != fixture.Valid {
t.Fatalf("valid = %v, want %v", valid, fixture.Valid)
}
})
}
}

// Reconstruct the Go bootstrap input and check the Python-managed projection
// against its owning types. Credentials travel only in RuntimeBootstrap.
func validManagedExchange(data []byte) bool {
var fields map[string]json.RawMessage
if json.Unmarshal(data, &fields) != nil {
return false
}
bootstrapBytes, _ := json.Marshal(sandbox.Bootstrap{})
var expected map[string]json.RawMessage
_ = json.Unmarshal(bootstrapBytes, &expected)
delete(expected, "CoreURL")
delete(expected, "Credential")
expected["InstallationID"] = nil
expected["RuntimeBootstrap"] = nil
if len(fields) != len(expected) {
return false
}
for name := range expected {
if _, ok := fields[name]; !ok {
return false
}
}
var installation string
if json.Unmarshal(fields["InstallationID"], &installation) != nil || !validID(installation) {
return false
}
delete(fields, "InstallationID")
delete(fields, "RuntimeBootstrap")
data, _ = json.Marshal(fields)
var bootstrap sandbox.Bootstrap
if json.Unmarshal(data, &bootstrap) != nil || !validReference(bootstrap.Reference) || !validID(bootstrap.DeviceID) || !validID(bootstrap.SessionID) {
return false
}
return (agentnetwork.Policy{Access: bootstrap.NetworkAccess, AllowedDomains: bootstrap.AllowedDomains}).Validate() == nil
}
4 changes: 4 additions & 0 deletions services/core/internal/sandbox/e2b/helper_sdk_generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading