MadeEasy

Self-host MadeEasy

Run the whole platform on your own infrastructure from prebuilt images, step by step. No source code required.

Prefer the cloud? MadeEasy is hosted for you.

You do not have to run anything yourself. MadeEasy is available as a managed cloud service, kept up to date and monitored by us. Start there and self-host later if you need to.

Open MadeEasy Cloud →

This guide walks a systems administrator through deploying MadeEasy on your own servers. You pull a prebuilt image from our registry, provide configuration, and start it. There is no source checkout. Two supported paths:

  • Docker Compose: the fastest path, for a single Docker host or VM.
  • Kubernetes (Helm): for teams that run a cluster.

What you will need

  • A host you control: a Linux VM with Docker and the Docker Compose plugin, or a Kubernetes cluster with kubectl and Helm 3.
  • A license key from AgenticAuto (see Step 1).
  • A PostgreSQL database with the vector extension. For the best knowledge-base search (BM25), use ParadeDB, which bundles it. The Docker Compose bundle runs this for you automatically; for Kubernetes you point at your own database.
  • At least one LLM provider key, such as Google Gemini (an API key, or a Vertex AI service account), OpenAI or Amazon Bedrock. You can put it in the configuration to start with, then keep it inside MadeEasy, encrypted, per organization (see Step 6).
  • Somewhere to keep files that outlive a run (uploads, reports a run saves): a folder on the host's disk (Docker Compose sets this up below) or a cloud bucket (Amazon S3, Google Cloud Storage, Azure Blob). Without one, a run's files are removed when it ends.
  • Memory to match your files. As a guide, 4 GB to start. A process that works through very large spreadsheets needs, on top of that, about as much memory as the largest single sheet it reads, so plan more (for example 8 GB) for workbooks of several gigabytes.
  • Outbound internet access from the host to pull the image and reach your LLM provider. To install the packages your tools need, also allow pypi.org, files.pythonhosted.org and api.osv.dev (see Step 6.4).

Step 1: Get your license key

MadeEasy for production requires a signed license key. Request one by emailing support.info@agenticauto.net or through Book a demo. You will receive a single long string; keep it secret. You will paste it into your configuration as MADEEASY_LICENSE_KEY with LICENSE_ENFORCEMENT=true.

For a short evaluation without a key, you may set LICENSE_ENFORCEMENT=false. Production use requires a valid key.

Step 2 (Option A): Deploy with Docker Compose

2.1 Install Docker

On a fresh Ubuntu/Debian host:

curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker "$USER"   # log out and back in so this takes effect
docker version                    # confirm Docker is running

2.2 Create a project folder

mkdir -p ~/madeeasy && cd ~/madeeasy

2.3 Create docker-compose.yml

Save the following as docker-compose.yml in that folder:

services:
  db:
    image: paradedb/paradedb:latest
    restart: unless-stopped
    environment:
      POSTGRES_USER: madeeasy
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}
      POSTGRES_DB: madeeasy
    volumes: [db-data:/var/lib/postgresql/data]
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U madeeasy -d madeeasy"]
      interval: 5s
      timeout: 5s
      retries: 20

  api:
    image: ${MADEEASY_IMAGE:-ghcr.io/cauchymike/madeeasy:latest}
    restart: unless-stopped
    env_file: .env
    environment:
      DATABASE_URL: postgresql://madeeasy:${POSTGRES_PASSWORD}@db:5432/madeeasy
      SECRETS_PROVIDER: env
      SERVICE_ROLE: api
    ports: ["${API_PORT:-8000}:8000"]
    volumes: [files:/files]
    depends_on:
      db: { condition: service_healthy }
    healthcheck:
      test: ["CMD-SHELL", "curl -fsS http://localhost:8000/health || exit 1"]
      interval: 15s
      timeout: 5s
      retries: 10
      start_period: 60s

  worker:
    image: ${MADEEASY_IMAGE:-ghcr.io/cauchymike/madeeasy:latest}
    restart: unless-stopped
    env_file: .env
    environment:
      DATABASE_URL: postgresql://madeeasy:${POSTGRES_PASSWORD}@db:5432/madeeasy
      SECRETS_PROVIDER: env
      SERVICE_ROLE: worker
    volumes: [deps:/deps, files:/files]
    depends_on:
      db: { condition: service_healthy }

volumes:
  db-data:
  deps:
  files:
One image runs two roles: api serves the app and, on start, applies database migrations and the task-queue schema; worker runs background jobs and waits for the api to finish provisioning, so no manual ordering is needed. Both mount the same files volume, where the files your processes keep are stored; back it up with the database.

2.4 Generate your secrets

Run these and copy each value for the next step:

# Master key that encrypts per-process secrets:
docker run --rm python:3.11-slim sh -c "pip -q install cryptography && python -c 'from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())'"

# Two signing secrets: one for sign-in, one for the platform operator (run twice):
openssl rand -base64 48

2.5 Create .env

Save this as .env in the same folder and fill in every value:

# Registry image (pin to a released tag in production, e.g. :1.4.0)
MADEEASY_IMAGE=ghcr.io/cauchymike/madeeasy:latest

# Database password for the bundled Postgres
POSTGRES_PASSWORD=choose-a-long-random-password

# Public host port the API listens on
API_PORT=8000

# License (from Step 1)
LICENSE_ENFORCEMENT=true
MADEEASY_LICENSE_KEY=paste-your-license-key

# Secrets (from Step 2.4)
PROCESS_SECRETS_MASTER_KEY=paste-fernet-key
AUTH_JWT_SECRET=paste-first-openssl-value
AUTH_ENABLED=true

# Platform operator (super admin) accounts, see Step 5 (second openssl value)
SUPER_ADMIN_SIGNING_KEY=paste-third-openssl-value

# LLM provider (at least one). You can move these into MadeEasy later (Step 6).
DEFAULT_LLM_PROVIDER=gemini
GEMINI_API_KEY=your-gemini-key
# GEMINI_DEFAULT_MODEL=gemini-3.1-flash-lite   # the model used where an agent names none
# OPENAI_API_KEY=your-openai-key

# Where files that outlive a run are kept (the files volume in docker-compose.yml)
WORKSPACE_REMOTE=folder
WORKSPACE_BUCKET=/files

# Tool calls: the most memory one call may use, in bytes (large spreadsheets need more).
# Keep it below the memory the container has. 0 sets no limit.
# TOOL_ISOLATED_MEMORY_BYTES=3221225472

# Worker + runtime tool dependencies (leave as-is)
WORKER_CONCURRENCY=5
WORKER_WATCHDOG_TIMEOUT_SECONDS=120
DEPENDENCY_INSTALLER=pip_target
DEPENDENCY_TARGET_DIR=/deps
KNOWLEDGE_BM25_MODE=auto

# If you serve the UI on a public domain, set it here (Step 4)
# API_CORS_ORIGINS=https://app.yourcompany.com
Keep .env private. Never commit it or share it.

2.6 Start it

docker compose up -d      # pulls images and starts db, api, worker
docker compose ps         # all should be "running"/"healthy"
docker compose logs -f api   # watch the api migrate and come up (Ctrl-C to stop)

Confirm the API is healthy:

curl -fsS http://localhost:8000/health   # -> {"status":"ok"} (200)

Step 2 (Option B): Deploy with Kubernetes (Helm)

Use this instead of Compose if you run Kubernetes. You will need kubectl pointed at your cluster and Helm 3. This path expects an external PostgreSQL (with vector, ideally ParadeDB for BM25).

B.1 Create a values file

Save as my-values.yaml:

image:
  repository: ghcr.io/cauchymike/madeeasy
  tag: "latest"        # pin to a release in production

database:
  url: "postgresql://user:password@your-db-host:5432/madeeasy"

license:
  enforce: true
  key: "paste-your-license-key"

secrets:
  processSecretsMasterKey: "paste-fernet-key"
  authJwtSecret: "paste-openssl-value-1"
  superAdminSigningKey: "paste-openssl-value-2"

llm:
  defaultProvider: gemini
  geminiApiKey: "your-gemini-key"

# Expose on your domain via your ingress controller (bring your own TLS)
ingress:
  enabled: true
  className: "nginx"
  host: "app.yourcompany.com"
  tls:
    enabled: true
    secretName: "madeeasy-tls"

B.2 Install

helm install madeeasy ./madeeasy -n madeeasy --create-namespace -f my-values.yaml

# Watch the rollout
kubectl -n madeeasy rollout status deploy/madeeasy-madeeasy-api
kubectl -n madeeasy rollout status deploy/madeeasy-madeeasy-worker
The chart deploys the api and worker, a Service, an optional Ingress, and a persistent volume for runtime tool installs. It does not run a database: point database.url at your own PostgreSQL. Contact us for the chart package if you do not have it.

Step 3: Create the first admin user

The image ships with the management commands. Create your first admin from inside the running api container.

Docker Compose:

docker compose exec api python scripts/create_admin.py \
  --username you@yourcompany.com \
  --email you@yourcompany.com \
  --full-name "Your Name"
# You will be prompted for a password (or pass --password).

Kubernetes:

kubectl -n madeeasy exec deploy/madeeasy-madeeasy-api -- \
  python scripts/create_admin.py --username you@yourcompany.com --email you@yourcompany.com --full-name "Your Name" --password 'a-strong-password'

Now open the app and sign in with that account.

Step 4: Put it on your domain (HTTPS)

With Docker Compose the API is exposed on a plain host port (API_PORT). Serve it on your own domain over HTTPS with whatever reverse proxy you already run. Two examples:

nginx

server {
  server_name app.yourcompany.com;
  location / {
    proxy_pass http://127.0.0.1:8000;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
  }
  # add TLS (e.g. with certbot)
}

Caddy (automatic HTTPS)

app.yourcompany.com {
  reverse_proxy 127.0.0.1:8000
}

Then set API_CORS_ORIGINS=https://app.yourcompany.com in .env and run docker compose up -d again. On Kubernetes, the Ingress in your values file handles this.

Make sure your proxy forwards X-Forwarded-For (the nginx snippet above does; Caddy does automatically). The app trusts it to see the real client IP, which it uses for per-IP rate limiting and request logging; otherwise every request looks like it comes from the proxy.
Behind a proxy on the same host, you can bind the API to localhost only: change the api port line to "127.0.0.1:${API_PORT:-8000}:8000" so it is not reachable directly from the internet.

Step 5: Set up the platform operator (super admin)

The super admin looks after the infrastructure MadeEasy runs on. When something goes wrong there (a misconfigured key, the database, a provider being down), your users see only a short message with a reference such as ME-AF72AC82; the details go to the super admin on the Platform health page.

It is deliberately separate from your admins:

  • A super admin sees only Platform health and Account security. The server refuses it every process, run, conversation, knowledge base and user list, so it never sees your organisation's work.
  • No one can create a super admin from the app, including admins. It can only be created with a command on this server, by someone with access to it.
  • Two-factor authentication is mandatory, at most 2 can be active, and every super-admin action goes into a tamper-evident trail, with an email to every super admin.

5.1 Check the signing key is set

You set SUPER_ADMIN_SIGNING_KEY in Step 2. The api and the worker must have the same value. Optional: SUPER_ADMIN_MAX (default 2) and SUPER_ADMIN_SESSION_MINUTES (default 60). To receive the security alerts, configure email (the MAIL_* settings); without it everything works and is recorded, but no alert is sent.

5.2 Create the first super admin

Docker Compose:

docker compose exec api python scripts/create_super_admin.py \
  --username ops --email you@yourcompany.com

Kubernetes:

kubectl -n madeeasy exec -it deploy/madeeasy-madeeasy-api -- \
  python scripts/create_super_admin.py --username ops --email you@yourcompany.com
  1. Type a password (at least 12 characters) when asked, then again to confirm. It is never shown and never goes on the command line.
  2. The command prints a recovery key, once. Store it offline now (a password manager vault, or printed and locked away). MadeEasy keeps only a scrambled copy and can never show it again.
  3. The --email mailbox receives the security alerts.
Every later super-admin action on the server (adding one, removing one, replacing the key) asks for the recovery key and prints a new one. Always keep the newest.

5.3 Sign in and set up two-factor authentication

Any authenticator app that makes time-based 6-digit codes works: Google Authenticator, Microsoft Authenticator, Authy, 1Password, Bitwarden and others.

  1. Install an authenticator app on your phone.
  2. Open MadeEasy and sign in with the super-admin username and password.
  3. You land on Platform health, which asks you to set up two-factor authentication (nothing else is shown until you do). Click Set it up now.
  4. Click Set up two-factor authentication. A QR code appears (drawn in your browser; the secret goes to no other service).
  5. In the app, choose add account and scan a QR code. Can't scan? Choose enter a setup key and type the key shown under the QR code (time-based).
  6. Type the current 6-digit code from the app and click Turn on. You are taken to Platform health.

From then on, each sign-in asks for the password and then the current code. A super-admin sign-in lasts at most 60 minutes. If a code is refused, wait for the next one and make sure your phone sets its clock automatically.

5.4 Day to day

# Show how many super admins are active and whether a recovery key is set
docker compose exec api python scripts/create_super_admin.py --status

# Add a second one (asks for the recovery key, prints a new one)
docker compose exec api python scripts/create_super_admin.py --username ops2 --email someone@yourcompany.com

# Remove one (signs it out everywhere at once)
docker compose exec api python scripts/create_super_admin.py --revoke ops2

# Replace the recovery key (if anyone may have seen it)
docker compose exec api python scripts/create_super_admin.py --rotate-recovery-key
  • Lost phone or forgotten password: there is no in-app reset, by design. Revoke the account and create a replacement with a new username, then set up two-factor authentication again.
  • Lost recovery key: only someone with direct database access can clear it, with DELETE FROM platform_recovery_key;. The next create then prints a new key. Check the trail and revoke any account you don't recognise.
  • The trail: the bottom of Platform health shows Trail intact, or Trail altered at entry X if someone edited the database directly. Treat the second as a security incident.
Network restrictions on who can reach the app (a VPN, an IP allowlist on your proxy) are your choice as the operator; MadeEasy does not require one. The full step-by-step guide is also in the in-app Help.

Step 6: Production settings

MadeEasy works with the settings above. These make it ready for real use. Change any of them in .env (or extraEnv in your Helm values) and run docker compose up -d again.

6.1 Keep the model provider keys inside MadeEasy

Instead of keeping provider keys in .env, store them in MadeEasy, encrypted under your master key, so an administrator can replace or test one without a restart:

# Copy the keys from the configuration into MadeEasy, encrypted, and test each one
docker compose exec api python scripts/org_llm_keys.py import
docker compose exec api python scripts/org_llm_keys.py status   # every provider should read as working

Then add LLM_KEY_SOURCE=organization to .env, run docker compose up -d, check that runs and the assistant work as before, and remove the *_API_KEY lines from .env. From then on keys are set and tested in the app, on the organization's page under AI providers, and never shown again once saved.

6.2 Files

With Docker Compose, files are kept in the files volume (set in Step 2). On Kubernetes, use a cloud bucket: set WORKSPACE_REMOTE to s3, gcs or azure_blob and WORKSPACE_BUCKET to the bucket's name through extraEnv, with the bucket's credential in your secrets. An organization can also be given its own storage in the app (organization page, File storage). For people to upload very large files straight from the browser to a bucket, allow your MadeEasy address, the method PUT and the header Content-Type in the bucket's CORS settings; otherwise uploads go through MadeEasy. WORKSPACE_THROUGH_UPLOAD_MAX_BYTES caps how large a file MadeEasy will carry itself (0, the default, sets no cap).

6.3 What is on by default for security

  • Closed to the public: the API's technical description (API_DOCS_ENABLED=true opens it, for development only) and the metrics, which are served only with METRICS_TOKEN (set one if you scrape metrics).
  • Request limits: 300 requests a minute per signed-in person, 30 a minute that start work, 60 a minute for someone not signed in, 3 password resets an hour. Each is a setting (RATE_LIMIT_GENERAL_PER_MINUTE, RATE_LIMIT_WORK_PER_MINUTE, RATE_LIMIT_ANONYMOUS_PER_MINUTE, RATE_LIMIT_PASSWORD_RESET_PER_HOUR); RATE_LIMIT_ENABLED=false turns them off.
  • Sign-in: after 5 failed attempts for one account (20 from one address) each further attempt waits, from a minute up to 15 minutes. Accounts are never locked.
  • Passwords: at least 12 characters (AUTH_PASSWORD_MIN_LENGTH), and checked against known breached passwords. Only the first five characters of a scrambled form of the password leave the server. On a server with no internet access, set AUTH_PASSWORD_BREACH_CHECK=false.
  • Sessions last 12 hours (AUTH_JWT_TTL_MINUTES, 720) and end at once, everywhere, when a user's password or role changes or they are switched off.
  • Browser protections are sent with every page. The content security policy starts in report-only mode: watch the api log for "Content security policy report" through normal use (including an upload and your chat widget), and when there are none, set SECURITY_CSP_ENFORCE=true. Put MadeEasy on HTTPS first (Step 4).
  • Request size: a request body over 16 MiB is refused (API_MAX_BODY_BYTES); file uploads have their own limits.

6.4 How tools run, and what they may install

Every call of a tool your processes use runs in a process of its own, as a separate system user (toolcall, set up in the image) with none of MadeEasy's settings. It reaches only its own process's files and the credentials its code names, connects only to the hosts its code names, and is stopped at its time and memory limits. Nothing needs setting for this, with one condition:

  • Keep the worker's SETUID and SETGID capabilities. The worker needs them to start each call as toolcall. Docker gives them by default. On Kubernetes, do not drop them in a security context; a cluster that enforces the "restricted" pod security standard drops every capability, and every tool call is then refused. A tool never runs as the worker's own user instead.
  • Packages an administrator approves are pinned to their exact files, checked against the public vulnerability database (osv.dev), and installed into the deps volume, so they survive a restart. If the check cannot be made, nothing is installed.
  • Portable software a tool needs (for example a Java runtime) is downloaded from its vendor when approved, checked against the vendor's published fingerprint, and kept in the same volume. Allow outbound access to that vendor.
  • Operating-system libraries are never installed by MadeEasy; a tool that needs one waits for a release of MadeEasy that includes it. Contact support.

6.5 Uploaded files and malware scanning (optional)

Two checks are always on: a file's type is judged from its content (a chat attachment must really be a photo, recording, video, PDF, Office document or text, so a program renamed photo.png is refused), and PDF and Word documents are read in a process of their own, with no network and a time and memory limit (DOCUMENT_READ_SECONDS, DOCUMENT_READ_MEMORY_BYTES).

Malware scanning is optional; MadeEasy ships no scanner of its own. Choose one:

  • Your own antivirus. Most enterprise antivirus products accept files over ICAP. Set MALWARE_SCANNER=icap, MALWARE_SCANNER_HOST and, from your product's documentation, MALWARE_SCANNER_ICAP_SERVICE and MALWARE_SCANNER_ICAP_MODE (respmod or reqmod). Nothing extra runs.
  • ClamAV, if you have none. With Docker Compose run docker compose --profile scanning up -d and set MALWARE_SCANNER=clamav and MALWARE_SCANNER_HOST=clamav; with Helm set scanning.scanner=clamav and scanning.clamav.enabled=true. It keeps its virus signatures in memory (about 1.5 GB) and updates them daily from database.clamav.net.
  • None (the default): files are not scanned, and the platform health page says so.

With a scanner set, every uploaded file is scanned before it is kept or read. An infected file is refused with the threat's name and recorded on the health page with who sent it. A file the scanner cannot check is refused for now, never let through, so keep the scanner running. A large file sent straight to your storage bucket is held until it is checked; the Files tab shows it as being checked.

6.6 Company sign-in (optional)

Each organization can let its invited people sign in with their work account (Google Workspace, Microsoft, Okta, or any OpenID Connect provider), set up by its administrator in Admin → Company sign-in. Nothing is configured on the server for it; it needs outbound HTTPS to the organization's provider and working DNS lookups (each organization confirms its email domain with a DNS record, checked against your server's own DNS).

6.7 Several organizations on one installation (optional)

An installation starts with one organization, and that is all most need. If one installation must keep several organizations' data apart (for example several companies in a group), MadeEasy can enforce it in the database itself. It is a deliberate, reversible change in four steps, made with scripts/org_scoping.py; contact support for the walkthrough before you start.

Using a secrets manager (AWS / Azure)

By default MadeEasy reads configuration from environment variables (SECRETS_PROVIDER=env). It can instead read from a managed vault:

  • AWS Secrets Manager: set SECRETS_PROVIDER=aws and give the workload an IAM role (or IRSA on EKS) that can read the secrets.
  • Azure Key Vault: set SECRETS_PROVIDER=azure and AZURE_VAULT_URL, with access via workload identity.

Store DATABASE_URL, MADEEASY_LICENSE_KEY, PROCESS_SECRETS_MASTER_KEY, the auth secrets, SUPER_ADMIN_SIGNING_KEY and your provider keys in the vault by name. In Helm, set config.secretsProvider=aws (or azure) and leave the secrets, database.url and license.key values empty; no Kubernetes Secret is created in that mode.

Upgrading

Docker Compose: change MADEEASY_IMAGE to the new tag, then:

docker compose pull && docker compose up -d

Kubernetes:

helm upgrade madeeasy ./madeeasy -n madeeasy --set image.tag=NEW_TAG -f my-values.yaml

The api role applies any new database migrations automatically on start. Pin to explicit released tags rather than latest so upgrades are deliberate.

Embed a chat widget on your site

Your instance also serves an embeddable chat widget, so you can put any of your conversational processes on your own website as a chat bubble, with no front-end build required. The widget script is served by your deployment at /widget.js.

  1. In the app, build a conversational process, ground it in a knowledge base, and promote it to Live.
  2. Open the process → Settings → Real-time trigger, click Create trigger, and copy the token (shown once).
  3. Still there, under Public widget access, add your website's origin to Allowed origins and set a per-IP rate limit. This locks the token to your site.
  4. Paste one tag before </body> on your site:
<script src="https://app.yourcompany.com/widget.js"
        data-process="YOUR_PROCESS_TYPE"
        data-token="PASTE_THE_TRIGGER_TOKEN"
        data-title="Support"
        data-accent="#4f7cff"></script>
The token is public by design but useless off your sites: the allowed-origins list rejects a browser from any other origin, and the rate limit bounds abuse. If a token is ever misused, regenerate it in the same panel. Developers who prefer to build their own chat UI can call the trigger endpoint directly (see the in-app Help for the request/response contract).

Troubleshooting

SymptomLikely cause & fix
API container exits immediately with a license messageMissing or invalid MADEEASY_LICENSE_KEY while LICENSE_ENFORCEMENT=true. Paste the key, or set enforcement off for evaluation.
/health not responding yetOn first start the api migrates the database; give it up to a minute. Check docker compose logs api.
Worker keeps restarting at first bootNormal until the api has provisioned the database and queue schema; it settles on its own.
Platform health says "Platform operator privileges required" for the super adminSUPER_ADMIN_SIGNING_KEY is missing or differs from the one the account was created with. Set the original value on the api and worker, or revoke and recreate the account.
Two-factor code is always refusedThe phone's clock is off. Set it to update automatically, then use the next code.
Uploaded files or a run's reports disappear after the runNo file store is set. Set WORKSPACE_REMOTE and WORKSPACE_BUCKET on the api and the worker (Step 2.5 or 6.2), with the same folder or bucket for both.
"Too many requests" during normal workA request limit is too low for your use. Raise the relevant RATE_LIMIT_* setting (Step 6.3).
A new password is refused as known from a breach, on a server with no internetThe check could not run as intended. Set AUTH_PASSWORD_BREACH_CHECK=false, or allow outbound access to api.pwnedpasswords.com.
A run on a large spreadsheet is stopped "over its memory limit"The tool needed more than TOOL_ISOLATED_MEMORY_BYTES. Raise it, staying below the container's memory, or give the host more memory.
Uploads are refused: "could not be checked for malware just now"A scanner is set (MALWARE_SCANNER) but is not answering. Check that it is running and reachable at MALWARE_SCANNER_HOST; the health page's "Uploaded files are scanned for malware" check says why. ClamAV takes a minute or two to load its signatures after a start.
Every tool call fails with "This worker cannot start a tool call as 'toolcall'"The worker lost its SETUID/SETGID capabilities, usually a Kubernetes security context or pod security policy that drops all capabilities. Allow those two on the worker (Step 6.4).
Approving a tool says the packages "couldn't be checked for known vulnerabilities"The server cannot reach api.osv.dev. Allow outbound access to it, and to pypi.org and files.pythonhosted.org, then approve again.
A run's tool reports "Network access refused"The tool tried to reach a host its code does not name, or a private address. Write the service's full address into the tool's code (or into a secret it reads) and approve the change.
"A credential must be set" for a providerKeys are kept in MadeEasy (LLM_KEY_SOURCE=organization) and this organization has none for that provider. Set it on the organization's page under AI providers.
Cannot pull the imageCheck outbound access to ghcr.io. For a private image, run docker login ghcr.io first.

Support

Questions or a license request: support.info@agenticauto.net. Prefer not to run it yourself? MadeEasy Cloud is ready to go.