Skip to content
Draft
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
9 changes: 6 additions & 3 deletions assets/sourceos/bin/turtle-agent-machine
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,24 @@
# TurtleTerm Agent Machine bridge.
set -eu

bin_dir="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
agentctl="$bin_dir/turtle-agentctl"

cmd="${1:-surfaces}"
case "$cmd" in
surfaces)
shift || true
exec turtle-agentctl --stdio agent-machine-surfaces "$@"
exec "$agentctl" --stdio agent-machine-surfaces "$@"
;;
probe)
shift || true
exec turtle-agentctl --stdio agent-machine-probe "$@"
exec "$agentctl" --stdio agent-machine-probe "$@"
;;
request-execution)
shift || true
surface="${1:-agent-machine/local-agentpod}"
shift || true
exec turtle-agentctl --stdio request-surface-execution "$surface" "$@"
exec "$agentctl" --stdio request-surface-execution "$surface" "$@"
;;
*)
echo "usage: turtle-agent-machine [surfaces|probe|request-execution <surface> -- <command>]" >&2
Expand Down
9 changes: 6 additions & 3 deletions assets/sourceos/bin/turtle-cloudfog
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,24 @@
# TurtleTerm CloudFog surface bridge.
set -eu

bin_dir="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
agentctl="$bin_dir/turtle-agentctl"

cmd="${1:-surfaces}"
case "$cmd" in
surfaces)
shift || true
exec turtle-agentctl --stdio cloudfog-surfaces "$@"
exec "$agentctl" --stdio cloudfog-surfaces "$@"
;;
inspect)
shift || true
exec turtle-agentctl --stdio cloudfog-inspect "$@"
exec "$agentctl" --stdio cloudfog-inspect "$@"
;;
request-execution)
shift || true
surface="${1:-cloudfog/local-devshell}"
shift || true
exec turtle-agentctl --stdio request-surface-execution "$surface" "$@"
exec "$agentctl" --stdio request-surface-execution "$surface" "$@"
;;
*)
echo "usage: turtle-cloudfog [surfaces|inspect <surface>|request-execution <surface> -- <command>]" >&2
Expand Down
7 changes: 5 additions & 2 deletions assets/sourceos/bin/turtle-superconscious
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@
# TurtleTerm Superconscious bridge.
set -eu

bin_dir="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
agentctl="$bin_dir/turtle-agentctl"

cmd="${1:-observe}"
case "$cmd" in
observe)
shift || true
exec turtle-agentctl --stdio superconscious-observe "$@"
exec "$agentctl" --stdio superconscious-observe "$@"
;;
propose)
shift || true
exec turtle-agentctl --stdio superconscious-propose "$@"
exec "$agentctl" --stdio superconscious-propose "$@"
;;
*)
echo "usage: turtle-superconscious [observe <text>|propose <prompt>]" >&2
Expand Down
75 changes: 75 additions & 0 deletions assets/sourceos/tests/test_cloudshell_fog_receipt_context.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#!/usr/bin/env python3
"""Validate TurtleTerm receipt context propagation for CloudShell FOG sessions."""

from __future__ import annotations

import json
import os
import subprocess
import sys
import tempfile
from pathlib import Path


REPO_ROOT = Path(__file__).resolve().parents[3]
TURTLE_WRAPPER = REPO_ROOT / "assets" / "sourceos" / "bin" / "turtle-term"


def read_ndjson(path: Path) -> list[dict]:
return [json.loads(line) for line in path.read_text(encoding="utf-8").splitlines() if line.strip()]


def main() -> int:
with tempfile.TemporaryDirectory() as tmp:
tmp_path = Path(tmp)
events = tmp_path / "events.ndjson"
receipts = tmp_path / "receipts"

env = dict(os.environ)
env.update(
{
"SOURCEOS_TERMINAL_SESSION_ID": "csf-session-0001",
"SOURCEOS_WORKSPACE": "workspace:lattice-demo",
"SOURCEOS_TERMINAL_EVENTS": str(events),
"SOURCEOS_TERMINAL_RECEIPTS": str(receipts),
"SOURCEOS_ACTOR_ID": "human:operator@example.com",
"SOURCEOS_POLICY_BUNDLE_ID": "policy:cloudshell-default",
"SOURCEOS_EXECUTION_DOMAIN": "cloudshell-fog/k8s",
}
)

result = subprocess.run(
[sys.executable, str(TURTLE_WRAPPER), "run", "--", sys.executable, "-c", "print('cloudshell-fog-ok')"],
cwd=str(REPO_ROOT),
env=env,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
check=False,
)

assert result.returncode == 0, result.stderr
assert "cloudshell-fog-ok" in result.stdout
assert events.exists(), "event stream missing"

rows = read_ndjson(events)
completed = [row for row in rows if row.get("event_type") == "command.completed"][-1]
receipt_path = Path(completed["receipt_path"])
assert receipt_path.exists(), f"receipt missing: {receipt_path}"

receipt = json.loads(receipt_path.read_text(encoding="utf-8"))
assert receipt["schema"] == "sourceos.terminal.receipt.v0"
assert receipt["session_id"] == "csf-session-0001"
assert receipt["workspace_id"] == "workspace:lattice-demo"
assert receipt["actor_id"] == "human:operator@example.com"
assert receipt["policy_bundle_id"] == "policy:cloudshell-default"
assert receipt["execution_domain"] == "cloudshell-fog/k8s"
assert receipt["stdout_digest"].startswith("sha256:")
assert receipt["stderr_digest"].startswith("sha256:")

print("validated CloudShell FOG receipt context propagation")
return 0


if __name__ == "__main__":
raise SystemExit(main())
86 changes: 86 additions & 0 deletions docs/integration/cloudshell-fog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# CloudShell FOG Integration Profile v0 — TurtleTerm

## Purpose

TurtleTerm is the SourceOS policy-aware, agent-addressable terminal workbench for trusted command execution, terminal receipts, agent delegation, and reproducible operator workflows.

CloudShell FOG is the browser/fog/cloud shell execution plane for session lifecycle, placement, PTY attach, runtime allocation, and audit.

This profile defines how TurtleTerm should integrate with CloudShell FOG while preserving TurtleTerm as the owner of terminal command receipt semantics.

## Ownership boundary

### TurtleTerm owns

- local/operator command lifecycle receipts
- SourceOS terminal session/event/receipt schemas
- stdout/stderr digest capture for command execution
- operator terminal event stream and receipt paths
- local agent gateway and terminal CLI behavior

### CloudShell FOG owns

- browser/fog/cloud shell session lifecycle
- WSS PTY attach contract
- fog/cloud placement metadata
- Kubernetes/fog runtime connector behavior
- CloudShell audit events
- runtime allocation and teardown semantics

## Integration principle

TurtleTerm should expose or preserve receipt metadata that allows CloudShell FOG sessions and audit events to correlate with local/operator command receipts.

TurtleTerm should not absorb CloudShell FOG's placement engine or runtime connector responsibilities.

## Environment propagation

When a TurtleTerm workflow is launched in the context of a CloudShell FOG session, the launcher MAY set:

- `SOURCEOS_TERMINAL_SESSION_ID` = CloudShell session ID or derived stable terminal session ID
- `SOURCEOS_WORKSPACE` = CloudShell workspace or project identifier, if known
- `SOURCEOS_ACTOR_ID` = CloudShell authenticated subject or mapped SourceOS actor identity
- `SOURCEOS_POLICY_BUNDLE_ID` = CloudShell policy/profile identifier
- `SOURCEOS_EXECUTION_DOMAIN` = `cloudshell-fog`, `k8s`, `fog`, or a more specific domain

TurtleTerm should preserve these values in session/event/receipt outputs rather than rewriting them with local-only defaults.

## Receipt correlation fields

Where available, CloudShell FOG metadata SHOULD be attached to TurtleTerm receipt context:

- CloudShell session ID
- CloudShell placement region
- CloudShell placement node ID
- CloudShell trust tier
- CloudShell placement reasons
- runtime image or runtime profile
- runtime namespace/pod identity when applicable

## Event mapping

| TurtleTerm / SourceOS terminal concept | CloudShell FOG concept |
|---|---|
| `sourceos.terminal.session.v0` | `session.created` / shell session context |
| `command.started` | command execution within attached shell context |
| `command.completed` | completed command plus receipt pointer |
| command stdout/stderr digests | command-level evidence, not PTY stream replacement |
| `execution_domain` | CloudShell runtime / placement execution domain |
| policy bundle ID | CloudShell policy/profile context |

## Non-goals

- TurtleTerm does not replace CloudShell FOG's browser shell or WSS PTY contract.
- TurtleTerm does not own CloudShell FOG's Kubernetes/fog placement engine.
- CloudShell FOG does not need to capture every PTY byte as a TurtleTerm command receipt by default.

## Open questions

1. Should SourceOS terminal schemas remain in TurtleTerm or move to a shared terminal-contracts repository?
2. Should TurtleTerm support a `cloudshell-fog` receipt enrichment mode with explicit placement fields?
3. Should AgentPlane become the canonical bridge for launching TurtleTerm-backed workflows from CloudShell FOG?

## Tracking

- TurtleTerm: issue #1
- CloudShell FOG: SocioProphet/cloudshell-fog#35
2 changes: 1 addition & 1 deletion packaging/scripts/build-rpm-package.sh
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ reproducible operator workflows.

%install
rm -rf %{buildroot}
TURTLE_TERM_STAGE_PREFIX=%{buildroot}/usr TURTLE_TERM_ETC_DIR=%{buildroot}/etc TURTLE_TERM_RUNTIME_PREFIX=/usr TURTLE_TERM_RUNTIME_ETC_DIR=/etc $repo_root/packaging/scripts/stage-linux-package.sh >/dev/null
TURTLE_TERM_STAGE_PREFIX=%{buildroot}/usr TURTLE_TERM_ETC_DIR=%{buildroot}/etc TURTLE_TERM_RUNTIME_PREFIX=/usr TURTLE_TERM_RUNTIME_ETC_DIR=/etc bash $repo_root/packaging/scripts/stage-linux-package.sh >/dev/null
cp $repo_root/LICENSE.md %{buildroot}/LICENSE.md
if [ -f $repo_root/THIRD_PARTY_NOTICES.md ]; then cp $repo_root/THIRD_PARTY_NOTICES.md %{buildroot}/THIRD_PARTY_NOTICES.md; fi

Expand Down