{
  "schemaVersion": "1.0",
  "title": "Run the CrewAI connector quickstart",
  "description": "In this tutorial, you will start the repository's deterministic CrewAI product path and route one governed run through the standalone Rust host and Python sidecar.",
  "canonical": "https://getaip.org/docs/connectors/crewai/getting-started/quickstart",
  "route": "/docs/connectors/crewai/getting-started/quickstart",
  "source": "docs/connectors/crewai/getting-started/quickstart.md",
  "protocol": "Agent Interoperability Protocol",
  "protocolVersion": "1.0",
  "section": "Connectors",
  "documentType": "Connector",
  "language": "en",
  "downloads": {
    "md": "/docs/download/connectors/crewai/getting-started/quickstart.md",
    "txt": "/docs/download/connectors/crewai/getting-started/quickstart.txt",
    "json": "/docs/download/connectors/crewai/getting-started/quickstart.json",
    "pdf": "/docs/download/connectors/crewai/getting-started/quickstart.pdf"
  },
  "content": {
    "format": "text/markdown",
    "markdown": "---\ntitle: Run the CrewAI connector quickstart\ndescription: >-\n  Start the deterministic CrewAI sidecar and host, approve one real crew run,\n  and inspect durable status and events\nkind: tutorial\naudience: application-developer\nappliesTo: \"1.x\"\nwritingStandard: \"aip-docs/1.0\"\nlastReviewedRevision: \"97be86e9efedf07ecf1783b03800f683f107fb04\"\nconnector: crewai\ncapabilityIds:\n  - cap:crewai:support-qualification\n  - cap:crewai:support-qualification:status\n  - cap:crewai:support-qualification:events\n---\n\n# Run the CrewAI connector quickstart\n\nIn this tutorial, you will start the repository's deterministic CrewAI product\npath and route one governed run through the standalone Rust host and Python\nsidecar.\n\nThe default product sidecar uses the frozen CrewAI `1.15.5` package set. Its\nnetwork-free qualification model emits deterministic real CrewAI events.\n\nThis is a controlled learning exercise. It is not a production deployment,\nexternal-model test, or complete qualification run.\n\n## What you will verify\n\nBy the end, you will have observed:\n\n- source-derived Rust-host and Python-sidecar image identities;\n- registry admission before the CrewAI host starts;\n- agreement between one host descriptor and one sidecar registry entry;\n- a signed run held for tenant-policy approval;\n- the same Action resumed with its stable idempotency key;\n- one terminal real-CrewAI result;\n- durable status and event replay keyed by the original Action ID;\n- cleanup limited to the named tutorial project.\n\nYou will not exercise batch, replay, train, test, knowledge, memory reset,\ncancellation races, storage outage, gateway failover, or an external provider.\n\n## Prerequisites\n\nYou need:\n\n- a clean `aip-core` checkout at the reviewed source revision;\n- Docker Engine, Docker Compose v2, and BuildKit;\n- network access required by the source-owned image build;\n- enough CPU, memory, and disk for all product-profile images;\n- TCP port `18443` free on loopback;\n- a POSIX-compatible shell, `lsof`, `grep`, and `sed`;\n- authority to remove the named project after reviewing its state.\n\nRun every command from the `aip-core` repository root. The products profile\nbuilds all product hosts even though this tutorial starts the CrewAI path.\n\nThe Compose project is `aip-connector-fleet-qualification`. Stop if another\ninvestigation or retained run owns that project or its volumes.\n\n## 1. Verify source and port ownership\n\nSet and verify the reviewed AIP revision:\n\n```sh\nexport AIP_SOURCE_REVISION=97be86e9efedf07ecf1783b03800f683f107fb04\n\ntest \"$(git rev-parse HEAD)\" = \"$AIP_SOURCE_REVISION\"\ntest -z \"$(git status --porcelain)\"\n```\n\nUse a separate clean checkout when either check fails. Never hide or discard\nlocal work to satisfy this tutorial.\n\nConfirm the required tools and fixed edge port:\n\n```sh\ndocker info >/dev/null\ndocker compose version\n\ncommand -v lsof >/dev/null\nif lsof -nP -iTCP:18443 -sTCP:LISTEN | grep -q .; then\n  echo \"Port 18443 is already in use\" >&2\n  exit 1\nfi\n```\n\nExpected result: the port check prints nothing. Identify an existing listener\nbefore changing it.\n\n## 2. Prepare controlled artifacts\n\nRun the source-owned preparation script:\n\n```sh\n./deploy/connector-fleet/prepare.sh\ntest -s deploy/connector-fleet/.env.qualification\n```\n\nExpected result: preparation ends with `connector fleet preparation: PASS`.\nThe owner-only environment file records source, image, release, and revision\nidentities selected by Compose.\n\nThe sidecar build uses the repository's default `Dockerfile` and frozen\n`uv.lock`. This product profile does not select the separate source-overlay\n`Dockerfile.upstream` path.\n\nDefine one helper for the source-owned profile:\n\n```sh\ncompose() {\n  docker compose \\\n    --profile products \\\n    --env-file deploy/connector-fleet/.env.qualification \\\n    -f deploy/connector-fleet/compose.yml \\\n    \"$@\"\n}\n\ncompose ps --all\n```\n\nStop if the fixed project contains state that must be retained.\n\n## 3. Start the CrewAI path\n\nStart the host and its declared dependencies:\n\n```sh\ncompose up --detach --wait --wait-timeout 300 crewai-acme-a\ncompose ps --all\n```\n\nExpected result: the CrewAI sidecar, host, both gateways, control plane, edge,\nand PostgreSQL are healthy. Initialization, migration, grant, and admission\njobs may be exited with status `0`.\n\nThe topology mounts one durable sidecar volume at `/var/lib/aip-crewai`. It\ndoes not start two active sidecars over that journal.\n\nDo not bypass a failed `admit-crewai` job or start the Rust host under a\ndifferent identity.\n\n## 4. Verify descriptor and registry agreement\n\nThe generated host descriptor admits `support-qualification` with all ten\noperations. The sidecar registry constructs the same crew ID.\n\nInspect only public generated configuration:\n\n```sh\ncompose exec -T crewai-acme-a \\\n  sh -c 'test -r /fleet-state/public/crewai-crews.json'\n\ncompose exec -T crewai-sidecar \\\n  python -c 'import importlib.metadata; print(importlib.metadata.version(\"crewai\"))'\n```\n\nExpected result: the descriptor file is readable and the sidecar reports\n`1.15.5`.\n\nDo not print the sidecar token or signing material. Host readiness already\nchecks authenticated sidecar health, durable journal presence, crew IDs, and\noperation coverage.\n\n## 5. Request one crew run\n\nCreate a private directory for tutorial evidence:\n\n```sh\nEVIDENCE_DIR=$(mktemp -d \"${TMPDIR:-/tmp}/aip-crewai-quickstart.XXXXXX\")\nchmod 700 \"$EVIDENCE_DIR\"\nprintf 'Tutorial evidence: %s\\n' \"$EVIDENCE_DIR\"\n```\n\nKeep one Action ID, key, and input for the entire run:\n\n```sh\nACTION_ID=act_crewai_quickstart_run_001\nIDEMPOTENCY_KEY=crewai-quickstart-run-v1\nINPUT='{\"case_id\":\"AIP-CREWAI-QUICKSTART\"}'\nPENDING=\"$EVIDENCE_DIR/run-pending.json\"\n```\n\nSubmit the run with the source-owned caller identity:\n\n```sh\ncompose run --rm --no-deps \\\n  --entrypoint /usr/local/bin/aipctl \\\n  -e AIPCTL_NATIVE_PRINCIPAL_ID=service:aipctl:qualification-acme \\\n  -e AIPCTL_NATIVE_TRUST_DOMAIN=fleet.test \\\n  -e AIPCTL_NATIVE_SIGNING_SEED_FILE=/fleet-state/secrets/client-acme-signing-seed.hex \\\n  -e AIPCTL_NATIVE_PEER_DID_FILE=/fleet-state/public/gateway.did \\\n  -e AIPCTL_NATIVE_TLS_CA_FILE=/caddy-data/caddy/pki/authorities/local/root.crt \\\n  qualification action call \\\n  https://aipd.fleet.test:8443 \\\n  cap:crewai:support-qualification \\\n  --action-id \"$ACTION_ID\" \\\n  --idempotency-key \"$IDEMPOTENCY_KEY\" \\\n  --input \"$INPUT\" >\"$PENDING\"\n\ngrep -q '\"status\": \"pending_approval\"' \"$PENDING\"\n```\n\nExpected result: the Action is pending approval. The sidecar has not been\nauthorized to start the crew.\n\nDo not change any of the three stable values after this step.\n\n## 6. Review and approve the run\n\nExtract and validate the approval identity and policy hash:\n\n```sh\napproval_id=$(sed -n \\\n  's/^[[:space:]]*\"approval_id\": \"\\([^\"]*\\)\",\\{0,1\\}$/\\1/p' \\\n  \"$PENDING\" | head -n 1)\npolicy_hash=$(sed -n \\\n  's/^[[:space:]]*\"policy_hash\": \"\\([^\"]*\\)\",\\{0,1\\}$/\\1/p' \\\n  \"$PENDING\" | head -n 1)\n\ncase \"$approval_id\" in\n  appr_[A-Za-z0-9_]*) ;;\n  *) echo \"Unexpected approval ID\" >&2; exit 1 ;;\nesac\n\ntest \"${#policy_hash}\" -eq 64\nprintf '%s' \"$policy_hash\" | grep -Eq '^[0-9a-f]{64}$'\n```\n\nReview the pending record. Stop if its Action, capability, input snapshot, or\npolicy differs from the request above.\n\nCreate one signed approval decision:\n\n```sh\ndecided_at=$(date -u +%Y-%m-%dT%H:%M:%SZ)\ndecision=$(printf \\\n  '{\"approval_id\":\"%s\",\"decision\":\"approved\",\"approver\":{\"id\":\"human:qualification-approver\",\"kind\":\"human\"},\"decided_at\":\"%s\",\"reason\":\"Approved for the CrewAI quickstart fixture\",\"constraints\":[],\"evidence\":[],\"decision_id\":\"decision:%s:approved\",\"policy_hash\":\"%s\",\"authority_path\":[]}' \\\n  \"$approval_id\" \"$decided_at\" \"$approval_id\" \"$policy_hash\")\n\ncompose run --rm --no-deps \\\n  --entrypoint /usr/local/bin/aipctl \\\n  -e AIPCTL_NATIVE_PRINCIPAL_ID=human:qualification-approver \\\n  -e AIPCTL_NATIVE_TRUST_DOMAIN=fleet.test \\\n  -e AIPCTL_NATIVE_SIGNING_SEED_FILE=/fleet-state/secrets/approval-acme-signing-seed.hex \\\n  -e AIPCTL_NATIVE_PEER_DID_FILE=/fleet-state/public/gateway.did \\\n  -e AIPCTL_NATIVE_TLS_CA_FILE=/caddy-data/caddy/pki/authorities/local/root.crt \\\n  qualification approval decide \\\n  https://aipd.fleet.test:8443 \\\n  \"$decision\" >\"$EVIDENCE_DIR/approval-decision.json\"\n\ngrep -q '\"kind\": \"aip.approval.granted\"' \\\n  \"$EVIDENCE_DIR/approval-decision.json\"\ngrep -q '\"kind\": \"aip.action.resumed_result\"' \\\n  \"$EVIDENCE_DIR/approval-decision.json\"\n```\n\nApproval authorizes this exact controlled run. It does not make model or tool\neffects retry-safe.\n\n## 7. Read the completed Action\n\nRepeat the exact Action to retrieve its durable result:\n\n```sh\nCOMPLETED=\"$EVIDENCE_DIR/run-completed.json\"\n\ncompose run --rm --no-deps \\\n  --entrypoint /usr/local/bin/aipctl \\\n  -e AIPCTL_NATIVE_PRINCIPAL_ID=service:aipctl:qualification-acme \\\n  -e AIPCTL_NATIVE_TRUST_DOMAIN=fleet.test \\\n  -e AIPCTL_NATIVE_SIGNING_SEED_FILE=/fleet-state/secrets/client-acme-signing-seed.hex \\\n  -e AIPCTL_NATIVE_PEER_DID_FILE=/fleet-state/public/gateway.did \\\n  -e AIPCTL_NATIVE_TLS_CA_FILE=/caddy-data/caddy/pki/authorities/local/root.crt \\\n  qualification action call \\\n  https://aipd.fleet.test:8443 \\\n  cap:crewai:support-qualification \\\n  --action-id \"$ACTION_ID\" \\\n  --idempotency-key \"$IDEMPOTENCY_KEY\" \\\n  --input \"$INPUT\" >\"$COMPLETED\"\n\ngrep -q '\"status\": \"completed\"' \"$COMPLETED\"\ngrep -q 'case triaged' \"$COMPLETED\"\n```\n\nExpected result: the signed result is terminal and contains the deterministic\nCrewAI output. The repeated request observes the same AIP intent.\n\nIf the result remains unknown, preserve the original identities. Do not create\na replacement run.\n\n## 8. Read durable sidecar status\n\nUse a new read Action that targets the original run Action ID:\n\n```sh\ncompose run --rm --no-deps \\\n  --entrypoint /usr/local/bin/aipctl \\\n  -e AIPCTL_NATIVE_PRINCIPAL_ID=service:aipctl:qualification-acme \\\n  -e AIPCTL_NATIVE_TRUST_DOMAIN=fleet.test \\\n  -e AIPCTL_NATIVE_SIGNING_SEED_FILE=/fleet-state/secrets/client-acme-signing-seed.hex \\\n  -e AIPCTL_NATIVE_PEER_DID_FILE=/fleet-state/public/gateway.did \\\n  -e AIPCTL_NATIVE_TLS_CA_FILE=/caddy-data/caddy/pki/authorities/local/root.crt \\\n  qualification action call \\\n  https://aipd.fleet.test:8443 \\\n  cap:crewai:support-qualification:status \\\n  --action-id act_crewai_quickstart_status_001 \\\n  --input '{\"run_action_id\":\"act_crewai_quickstart_run_001\"}' \\\n  >\"$EVIDENCE_DIR/run-status.json\"\n\ngrep -q '\"status\": \"completed\"' \"$EVIDENCE_DIR/run-status.json\"\ngrep -q 'act_crewai_quickstart_run_001' \"$EVIDENCE_DIR/run-status.json\"\n```\n\nThis reads the sidecar journal. It does not execute the crew again.\n\n## 9. Replay the event journal\n\nRead events from cursor zero:\n\n```sh\ncompose run --rm --no-deps \\\n  --entrypoint /usr/local/bin/aipctl \\\n  -e AIPCTL_NATIVE_PRINCIPAL_ID=service:aipctl:qualification-acme \\\n  -e AIPCTL_NATIVE_TRUST_DOMAIN=fleet.test \\\n  -e AIPCTL_NATIVE_SIGNING_SEED_FILE=/fleet-state/secrets/client-acme-signing-seed.hex \\\n  -e AIPCTL_NATIVE_PEER_DID_FILE=/fleet-state/public/gateway.did \\\n  -e AIPCTL_NATIVE_TLS_CA_FILE=/caddy-data/caddy/pki/authorities/local/root.crt \\\n  qualification action call \\\n  https://aipd.fleet.test:8443 \\\n  cap:crewai:support-qualification:events \\\n  --action-id act_crewai_quickstart_events_001 \\\n  --input '{\"run_action_id\":\"act_crewai_quickstart_run_001\",\"cursor\":0}' \\\n  >\"$EVIDENCE_DIR/run-events.json\"\n\ngrep -q '\"event\": \"completed\"' \"$EVIDENCE_DIR/run-events.json\"\n```\n\nExpected result: the bounded response includes ordered stored events and a\nnext cursor. This is journal replay, not live streaming.\n\n## Diagnose before cleanup\n\nCapture bounded state first:\n\n```sh\ncompose ps --all\ncompose logs --no-color --tail 200 \\\n  aipd aipd-b control-plane crewai-acme-a crewai-sidecar\n```\n\n| Symptom | First check |\n|---|---|\n| Preparation cannot reach Docker | Repair the daemon, then repeat source checks |\n| Port `18443` is occupied | Identify its owner without stopping it |\n| Sidecar is unhealthy | Token file, registry, journal, lock, and pinned image |\n| Host is unhealthy | Sidecar health, descriptor crew IDs, and operation policy |\n| Admission exits nonzero | Preserve the job log; do not bypass admission |\n| Run never leaves approval | Decision identity, policy hash, and Action correlation |\n| Run fails | Sidecar log by Action ID and terminal journal event |\n| Status returns not found | Exact original Action ID and sidecar state volume |\n| Event cursor is rejected | Use a cursor within the retained event journal |\n\nThe tutorial does not authorize printing credentials, weakening TLS, deleting\nthe journal, editing approval state, or bypassing the gateway.\n\n## Stop and remove the tutorial project\n\nOnly after confirming that no evidence or state must be retained, remove the\nnamed project's containers, network, and volumes:\n\n```sh\ncompose down --volumes --remove-orphans --timeout 30\ntest -z \"$(compose ps --all --quiet)\"\n```\n\nVolume removal permanently deletes PostgreSQL, generated identities, the\nsidecar journal, and training artifacts in this project. It does not prune\nunrelated Docker resources.\n\n## Related documentation\n\n- [CrewAI connector overview](../README.md)\n- [AIP quickstart](../../../getting-started/quickstart.md)\n- [Connector fleet quickstart](../../../getting-started/connector-fleet-quickstart.md)\n- [Deploy the connector fleet](../../../guides/deploy-connector-fleet.md)\n- [Errors and retry decisions](../../../reference/errors.md)\n",
    "text": "Run the CrewAI connector quickstart\n\nIn this tutorial, you will start the repository's deterministic CrewAI product\npath and route one governed run through the standalone Rust host and Python\nsidecar.\n\nThe default product sidecar uses the frozen CrewAI 1.15.5 package set. Its\nnetwork-free qualification model emits deterministic real CrewAI events.\n\nThis is a controlled learning exercise. It is not a production deployment,\nexternal-model test, or complete qualification run.\n\nWhat you will verify\n\nBy the end, you will have observed:\n• source-derived Rust-host and Python-sidecar image identities;\n• registry admission before the CrewAI host starts;\n• agreement between one host descriptor and one sidecar registry entry;\n• a signed run held for tenant-policy approval;\n• the same Action resumed with its stable idempotency key;\n• one terminal real-CrewAI result;\n• durable status and event replay keyed by the original Action ID;\n• cleanup limited to the named tutorial project.\n\nYou will not exercise batch, replay, train, test, knowledge, memory reset,\ncancellation races, storage outage, gateway failover, or an external provider.\n\nPrerequisites\n\nYou need:\n• a clean aip-core checkout at the reviewed source revision;\n• Docker Engine, Docker Compose v2, and BuildKit;\n• network access required by the source-owned image build;\n• enough CPU, memory, and disk for all product-profile images;\n• TCP port 18443 free on loopback;\n• a POSIX-compatible shell, lsof, grep, and sed;\n• authority to remove the named project after reviewing its state.\n\nRun every command from the aip-core repository root. The products profile\nbuilds all product hosts even though this tutorial starts the CrewAI path.\n\nThe Compose project is aip-connector-fleet-qualification. Stop if another\ninvestigation or retained run owns that project or its volumes.\n1. Verify source and port ownership\n\nSet and verify the reviewed AIP revision:\n\nexport AIPSOURCEREVISION=97be86e9efedf07ecf1783b03800f683f107fb04\n\ntest \"$(git rev-parse HEAD)\" = \"$AIPSOURCEREVISION\"\ntest -z \"$(git status --porcelain)\"\n\nUse a separate clean checkout when either check fails. Never hide or discard\nlocal work to satisfy this tutorial.\n\nConfirm the required tools and fixed edge port:\n\ndocker info >/dev/null\ndocker compose version\n\ncommand -v lsof >/dev/null\nif lsof -nP -iTCP:18443 -sTCP:LISTEN | grep -q .; then\n  echo \"Port 18443 is already in use\" >&2\n  exit 1\nfi\n\nExpected result: the port check prints nothing. Identify an existing listener\nbefore changing it.\n2. Prepare controlled artifacts\n\nRun the source-owned preparation script:\n\n./deploy/connector-fleet/prepare.sh\ntest -s deploy/connector-fleet/.env.qualification\n\nExpected result: preparation ends with connector fleet preparation: PASS.\nThe owner-only environment file records source, image, release, and revision\nidentities selected by Compose.\n\nThe sidecar build uses the repository's default Dockerfile and frozen\nuv.lock. This product profile does not select the separate source-overlay\nDockerfile.upstream path.\n\nDefine one helper for the source-owned profile:\n\ncompose() {\n  docker compose \\\n    --profile products \\\n    --env-file deploy/connector-fleet/.env.qualification \\\n    -f deploy/connector-fleet/compose.yml \\\n    \"$@\"\n}\n\ncompose ps --all\n\nStop if the fixed project contains state that must be retained.\n3. Start the CrewAI path\n\nStart the host and its declared dependencies:\n\ncompose up --detach --wait --wait-timeout 300 crewai-acme-a\ncompose ps --all\n\nExpected result: the CrewAI sidecar, host, both gateways, control plane, edge,\nand PostgreSQL are healthy. Initialization, migration, grant, and admission\njobs may be exited with status 0.\n\nThe topology mounts one durable sidecar volume at /var/lib/aip-crewai. It\ndoes not start two active sidecars over that journal.\n\nDo not bypass a failed admit-crewai job or start the Rust host under a\ndifferent identity.\n4. Verify descriptor and registry agreement\n\nThe generated host descriptor admits support-qualification with all ten\noperations. The sidecar registry constructs the same crew ID.\n\nInspect only public generated configuration:\n\ncompose exec -T crewai-acme-a \\\n  sh -c 'test -r /fleet-state/public/crewai-crews.json'\n\ncompose exec -T crewai-sidecar \\\n  python -c 'import importlib.metadata; print(importlib.metadata.version(\"crewai\"))'\n\nExpected result: the descriptor file is readable and the sidecar reports\n1.15.5.\n\nDo not print the sidecar token or signing material. Host readiness already\nchecks authenticated sidecar health, durable journal presence, crew IDs, and\noperation coverage.\n5. Request one crew run\n\nCreate a private directory for tutorial evidence:\n\nEVIDENCEDIR=$(mktemp -d \"${TMPDIR:-/tmp}/aip-crewai-quickstart.XXXXXX\")\nchmod 700 \"$EVIDENCEDIR\"\nprintf 'Tutorial evidence: %s\\n' \"$EVIDENCEDIR\"\n\nKeep one Action ID, key, and input for the entire run:\n\nACTIONID=actcrewaiquickstartrun001\nIDEMPOTENCYKEY=crewai-quickstart-run-v1\nINPUT='{\"caseid\":\"AIP-CREWAI-QUICKSTART\"}'\nPENDING=\"$EVIDENCEDIR/run-pending.json\"\n\nSubmit the run with the source-owned caller identity:\n\ncompose run --rm --no-deps \\\n  --entrypoint /usr/local/bin/aipctl \\\n  -e AIPCTLNATIVEPRINCIPALID=service:aipctl:qualification-acme \\\n  -e AIPCTLNATIVETRUSTDOMAIN=fleet.test \\\n  -e AIPCTLNATIVESIGNINGSEEDFILE=/fleet-state/secrets/client-acme-signing-seed.hex \\\n  -e AIPCTLNATIVEPEERDIDFILE=/fleet-state/public/gateway.did \\\n  -e AIPCTLNATIVETLSCAFILE=/caddy-data/caddy/pki/authorities/local/root.crt \\\n  qualification action call \\\n  https://aipd.fleet.test:8443 \\\n  cap:crewai:support-qualification \\\n  --action-id \"$ACTIONID\" \\\n  --idempotency-key \"$IDEMPOTENCYKEY\" \\\n  --input \"$INPUT\" >\"$PENDING\"\n\ngrep -q '\"status\": \"pendingapproval\"' \"$PENDING\"\n\nExpected result: the Action is pending approval. The sidecar has not been\nauthorized to start the crew.\n\nDo not change any of the three stable values after this step.\n6. Review and approve the run\n\nExtract and validate the approval identity and policy hash:\n\napprovalid=$(sed -n \\\n  's/^[[:space:]]\"approvalid\": \"\\([^\"]\\)\",\\{0,1\\}$/\\1/p' \\\n  \"$PENDING\" | head -n 1)\npolicyhash=$(sed -n \\\n  's/^[[:space:]]\"policyhash\": \"\\([^\"]\\)\",\\{0,1\\}$/\\1/p' \\\n  \"$PENDING\" | head -n 1)\n\ncase \"$approvalid\" in\n  appr[A-Za-z0-9]) ;;\n  ) echo \"Unexpected approval ID\" >&2; exit 1 ;;\nesac\n\ntest \"${#policyhash}\" -eq 64\nprintf '%s' \"$policyhash\" | grep -Eq '^[0-9a-f]{64}$'\n\nReview the pending record. Stop if its Action, capability, input snapshot, or\npolicy differs from the request above.\n\nCreate one signed approval decision:\n\ndecidedat=$(date -u +%Y-%m-%dT%H:%M:%SZ)\ndecision=$(printf \\\n  '{\"approvalid\":\"%s\",\"decision\":\"approved\",\"approver\":{\"id\":\"human:qualification-approver\",\"kind\":\"human\"},\"decidedat\":\"%s\",\"reason\":\"Approved for the CrewAI quickstart fixture\",\"constraints\":[],\"evidence\":[],\"decisionid\":\"decision:%s:approved\",\"policyhash\":\"%s\",\"authoritypath\":[]}' \\\n  \"$approvalid\" \"$decidedat\" \"$approvalid\" \"$policyhash\")\n\ncompose run --rm --no-deps \\\n  --entrypoint /usr/local/bin/aipctl \\\n  -e AIPCTLNATIVEPRINCIPALID=human:qualification-approver \\\n  -e AIPCTLNATIVETRUSTDOMAIN=fleet.test \\\n  -e AIPCTLNATIVESIGNINGSEEDFILE=/fleet-state/secrets/approval-acme-signing-seed.hex \\\n  -e AIPCTLNATIVEPEERDIDFILE=/fleet-state/public/gateway.did \\\n  -e AIPCTLNATIVETLSCAFILE=/caddy-data/caddy/pki/authorities/local/root.crt \\\n  qualification approval decide \\\n  https://aipd.fleet.test:8443 \\\n  \"$decision\" >\"$EVIDENCEDIR/approval-decision.json\"\n\ngrep -q '\"kind\": \"aip.approval.granted\"' \\\n  \"$EVIDENCEDIR/approval-decision.json\"\ngrep -q '\"kind\": \"aip.action.resumedresult\"' \\\n  \"$EVIDENCEDIR/approval-decision.json\"\n\nApproval authorizes this exact controlled run. It does not make model or tool\neffects retry-safe.\n7. Read the completed Action\n\nRepeat the exact Action to retrieve its durable result:\n\nCOMPLETED=\"$EVIDENCEDIR/run-completed.json\"\n\ncompose run --rm --no-deps \\\n  --entrypoint /usr/local/bin/aipctl \\\n  -e AIPCTLNATIVEPRINCIPALID=service:aipctl:qualification-acme \\\n  -e AIPCTLNATIVETRUSTDOMAIN=fleet.test \\\n  -e AIPCTLNATIVESIGNINGSEEDFILE=/fleet-state/secrets/client-acme-signing-seed.hex \\\n  -e AIPCTLNATIVEPEERDIDFILE=/fleet-state/public/gateway.did \\\n  -e AIPCTLNATIVETLSCAFILE=/caddy-data/caddy/pki/authorities/local/root.crt \\\n  qualification action call \\\n  https://aipd.fleet.test:8443 \\\n  cap:crewai:support-qualification \\\n  --action-id \"$ACTIONID\" \\\n  --idempotency-key \"$IDEMPOTENCYKEY\" \\\n  --input \"$INPUT\" >\"$COMPLETED\"\n\ngrep -q '\"status\": \"completed\"' \"$COMPLETED\"\ngrep -q 'case triaged' \"$COMPLETED\"\n\nExpected result: the signed result is terminal and contains the deterministic\nCrewAI output. The repeated request observes the same AIP intent.\n\nIf the result remains unknown, preserve the original identities. Do not create\na replacement run.\n8. Read durable sidecar status\n\nUse a new read Action that targets the original run Action ID:\n\ncompose run --rm --no-deps \\\n  --entrypoint /usr/local/bin/aipctl \\\n  -e AIPCTLNATIVEPRINCIPALID=service:aipctl:qualification-acme \\\n  -e AIPCTLNATIVETRUSTDOMAIN=fleet.test \\\n  -e AIPCTLNATIVESIGNINGSEEDFILE=/fleet-state/secrets/client-acme-signing-seed.hex \\\n  -e AIPCTLNATIVEPEERDIDFILE=/fleet-state/public/gateway.did \\\n  -e AIPCTLNATIVETLSCAFILE=/caddy-data/caddy/pki/authorities/local/root.crt \\\n  qualification action call \\\n  https://aipd.fleet.test:8443 \\\n  cap:crewai:support-qualification:status \\\n  --action-id actcrewaiquickstartstatus001 \\\n  --input '{\"runactionid\":\"actcrewaiquickstartrun001\"}' \\\n  >\"$EVIDENCEDIR/run-status.json\"\n\ngrep -q '\"status\": \"completed\"' \"$EVIDENCEDIR/run-status.json\"\ngrep -q 'actcrewaiquickstartrun001' \"$EVIDENCEDIR/run-status.json\"\n\nThis reads the sidecar journal. It does not execute the crew again.\n9. Replay the event journal\n\nRead events from cursor zero:\n\ncompose run --rm --no-deps \\\n  --entrypoint /usr/local/bin/aipctl \\\n  -e AIPCTLNATIVEPRINCIPALID=service:aipctl:qualification-acme \\\n  -e AIPCTLNATIVETRUSTDOMAIN=fleet.test \\\n  -e AIPCTLNATIVESIGNINGSEEDFILE=/fleet-state/secrets/client-acme-signing-seed.hex \\\n  -e AIPCTLNATIVEPEERDIDFILE=/fleet-state/public/gateway.did \\\n  -e AIPCTLNATIVETLSCAFILE=/caddy-data/caddy/pki/authorities/local/root.crt \\\n  qualification action call \\\n  https://aipd.fleet.test:8443 \\\n  cap:crewai:support-qualification:events \\\n  --action-id actcrewaiquickstartevents001 \\\n  --input '{\"runactionid\":\"actcrewaiquickstartrun001\",\"cursor\":0}' \\\n  >\"$EVIDENCEDIR/run-events.json\"\n\ngrep -q '\"event\": \"completed\"' \"$EVIDENCEDIR/run-events.json\"\n\nExpected result: the bounded response includes ordered stored events and a\nnext cursor. This is journal replay, not live streaming.\n\nDiagnose before cleanup\n\nCapture bounded state first:\n\ncompose ps --all\ncompose logs --no-color --tail 200 \\\n  aipd aipd-b control-plane crewai-acme-a crewai-sidecar\n\n| Symptom | First check |\n\n| Preparation cannot reach Docker | Repair the daemon, then repeat source checks |\n| Port 18443 is occupied | Identify its owner without stopping it |\n| Sidecar is unhealthy | Token file, registry, journal, lock, and pinned image |\n| Host is unhealthy | Sidecar health, descriptor crew IDs, and operation policy |\n| Admission exits nonzero | Preserve the job log; do not bypass admission |\n| Run never leaves approval | Decision identity, policy hash, and Action correlation |\n| Run fails | Sidecar log by Action ID and terminal journal event |\n| Status returns not found | Exact original Action ID and sidecar state volume |\n| Event cursor is rejected | Use a cursor within the retained event journal |\n\nThe tutorial does not authorize printing credentials, weakening TLS, deleting\nthe journal, editing approval state, or bypassing the gateway.\n\nStop and remove the tutorial project\n\nOnly after confirming that no evidence or state must be retained, remove the\nnamed project's containers, network, and volumes:\n\ncompose down --volumes --remove-orphans --timeout 30\ntest -z \"$(compose ps --all --quiet)\"\n\nVolume removal permanently deletes PostgreSQL, generated identities, the\nsidecar journal, and training artifacts in this project. It does not prune\nunrelated Docker resources.\n\nRelated documentation\n• CrewAI connector overview (../README.md)\n• AIP quickstart (../../../getting-started/quickstart.md)\n• Connector fleet quickstart (../../../getting-started/connector-fleet-quickstart.md)\n• Deploy the connector fleet (../../../guides/deploy-connector-fleet.md)\n• Errors and retry decisions (../../../reference/errors.md)\n"
  },
  "integrity": {
    "algorithm": "sha256",
    "sourceDigest": "c7480572480332bf83b6de5a3f187b5b95e4fa9ad2ce70c81c05a32feb1e23fc"
  }
}
