Installation¶
The model is plain SQL — there is nothing to compile and no runtime dependency beyond PostgreSQL itself.
Requirements¶
| Requirement | Notes |
|---|---|
| PostgreSQL 14+ | The schema deliberately avoids 15-only features so it loads on 14. Tested on 14–16. |
psql + createdb |
Ships with any PostgreSQL client install. |
A role that can CREATE DATABASE / CREATE SCHEMA |
No superuser required. |
The three SQL files (and load order)¶
Order matters — objects reference each other:
1. Sydonia/schema/asycuda.sql creates schema `asycuda` + 55 tables + indexes
2. Sydonia/schema/seed_reference.sql fills the ref_* / code tables
3. Sydonia/examples/e2e.sql (optional) a worked manifest → release example
Load them with ON_ERROR_STOP=1 so any problem fails loudly:
createdb customs_sandbox
psql -v ON_ERROR_STOP=1 -d customs_sandbox -f Sydonia/schema/asycuda.sql
psql -v ON_ERROR_STOP=1 -d customs_sandbox -f Sydonia/schema/seed_reference.sql
psql -v ON_ERROR_STOP=1 -d customs_sandbox -f Sydonia/examples/e2e.sql # optional
The asycuda schema¶
Everything is created inside a dedicated PostgreSQL schema (namespace)
called asycuda, not in public. This keeps the model self-contained and easy
to drop, and lets it coexist with your own tables.
-- from asycuda.sql
DROP SCHEMA IF EXISTS asycuda CASCADE; -- (1)!
CREATE SCHEMA asycuda;
SET search_path TO asycuda, public;
- The load is idempotent: re-running
asycuda.sqldrops and recreates the whole schema, so you always get a clean slate. Anything you put insideasycudais discarded on reload — keep your own tables elsewhere.
To use bare table names in a session, set the search path once:
SET search_path TO asycuda, public;
SELECT count(*) FROM declaration; -- instead of asycuda.declaration
To make it the default for a database or role:
Verify the load¶
SET search_path TO asycuda, public;
-- 55 base tables expected
SELECT count(*) FROM information_schema.tables
WHERE table_schema = 'asycuda' AND table_type = 'BASE TABLE';
-- reference data present (transport modes, statuses, lanes, …)
SELECT count(*) FROM ref_transport_mode; -- 9
SELECT code, name FROM ref_selectivity_lane ORDER BY code;
What a clean install looks like
asycuda.sql and seed_reference.sql complete with zero errors, the
table count is 55, and e2e.sql prints the declaration summary with
total_assessed = receipt_amount. Anything else is a real failure — read the
psql output.
Docker one-liner¶
docker run --name customs -e POSTGRES_PASSWORD=customs -p 5432:5432 -d postgres:16
export PGURL="postgresql://postgres:customs@localhost:5432"
createdb "$PGURL/customs_sandbox" 2>/dev/null || \
psql "$PGURL/postgres" -c 'CREATE DATABASE customs_sandbox;'
for f in schema/asycuda.sql schema/seed_reference.sql examples/e2e.sql; do
psql -v ON_ERROR_STOP=1 "$PGURL/customs_sandbox" -f "Sydonia/$f"
done
Troubleshooting¶
| Symptom | Cause / fix |
|---|---|
permission denied to create database |
Use a role with CREATEDB, or create the DB as an admin and load into it. |
schema "asycuda" already exists errors mid-file |
You edited the file and removed the DROP SCHEMA guard — restore it, or DROP SCHEMA asycuda CASCADE; first. |
relation "…" does not exist in the seed/e2e |
Files loaded out of order — always load asycuda.sql first. |
Bare table names "not found" in psql |
Run SET search_path TO asycuda, public; for the session. |
Syntax errors on GENERATED ALWAYS AS IDENTITY |
Your server is < PostgreSQL 10 — upgrade; the model targets 14+. |
Automate it with an agent¶
The customs-schema-setup Agent Skill does all of the
above for you — creating the database, loading the files in order, and reporting
a clean/failed result — and customs-validate re-runs the
full done-condition checks. The skills install into any agent (Claude Code,
Cursor, Codex, …) via npx skills add.