diff --git a/.gitignore b/.gitignore
index c603208..6ccf7dd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,6 @@
out/
+liquibase/liquibase.local.properties
+liquibase/lib/
devel/pg/src
devel/pg/1*
devel/pg/data*
diff --git a/cli/scripts/sql/get_party_description.sql b/cli/scripts/sql/get_party_description.sql
new file mode 100644
index 0000000..25c8db3
--- /dev/null
+++ b/cli/scripts/sql/get_party_description.sql
@@ -0,0 +1,31 @@
+-- Read-only PL/pgSQL helper for party.description.
+--
+-- Expected table (adjust names/types to match your schema):
+-- CREATE TABLE party (
+-- party_id bigint PRIMARY KEY,
+-- description text
+-- );
+
+CREATE OR REPLACE FUNCTION get_party_description(p_party_id bigint)
+RETURNS text
+LANGUAGE plpgsql
+STABLE
+STRICT
+PARALLEL SAFE
+SECURITY INVOKER
+SET search_path = ''
+AS $function$
+DECLARE
+ v_description text;
+BEGIN
+ SELECT p.description
+ INTO v_description
+ FROM public.party AS p
+ WHERE p.party_id = p_party_id;
+
+ RETURN v_description;
+END;
+$function$;
+
+COMMENT ON FUNCTION get_party_description(bigint) IS
+ 'Returns description for the given party_id; NULL if the row does not exist.';
diff --git a/liquibase/README.md b/liquibase/README.md
new file mode 100644
index 0000000..3558889
--- /dev/null
+++ b/liquibase/README.md
@@ -0,0 +1,59 @@
+# Liquibase
+
+Database migrations for PostgreSQL. **SQL files are plain PostgreSQL** — no Liquibase directives inside them. Liquibase metadata lives only in the small XML changelogs that reference those files via `sqlFile`.
+
+## Layout
+
+```
+liquibase/
+ liquibase.properties
+ changelog/
+ db.changelog-master.xml
+ changes/
+ 001-get-party-description.xml # changeset (sqlFile refs only)
+ 001-get-party-description.sql # native SQL
+ 001-get-party-description-rollback.sql # native rollback SQL
+```
+
+## Prerequisites
+
+- [Liquibase CLI](https://docs.liquibase.com/start/install/home.html)
+- PostgreSQL JDBC driver on the classpath (or Liquibase 4.29+ with bundled drivers)
+
+## Configure
+
+```bash
+cp liquibase.properties.example liquibase.local.properties
+# edit liquibase.local.properties with your JDBC URL and credentials
+```
+
+`liquibase.local.properties` is gitignored.
+
+## Run
+
+```bash
+cd liquibase
+liquibase update
+```
+
+Preview SQL:
+
+```bash
+liquibase update-sql
+```
+
+Rollback:
+
+```bash
+liquibase rollback-count 1
+```
+
+You can also run the native `.sql` files directly with `psql` if you are not using Liquibase.
+
+## Changesets
+
+| Id | Apply SQL | Rollback SQL |
+|----|-----------|--------------|
+| `001-get-party-description` | `changes/001-get-party-description.sql` | `changes/001-get-party-description-rollback.sql` |
+
+`splitStatements="false"` is set on `sqlFile` so PostgreSQL `$function$` delimiters are not split.
diff --git a/liquibase/changelog/changes/001-get-party-description-rollback.sql b/liquibase/changelog/changes/001-get-party-description-rollback.sql
new file mode 100644
index 0000000..fb36d3a
--- /dev/null
+++ b/liquibase/changelog/changes/001-get-party-description-rollback.sql
@@ -0,0 +1 @@
+DROP FUNCTION IF EXISTS public.get_party_description(bigint);
diff --git a/liquibase/changelog/changes/001-get-party-description.sql b/liquibase/changelog/changes/001-get-party-description.sql
new file mode 100644
index 0000000..4fd6a9c
--- /dev/null
+++ b/liquibase/changelog/changes/001-get-party-description.sql
@@ -0,0 +1,23 @@
+CREATE OR REPLACE FUNCTION public.get_party_description(p_party_id bigint)
+RETURNS text
+LANGUAGE plpgsql
+STABLE
+STRICT
+PARALLEL SAFE
+SECURITY INVOKER
+SET search_path = ''
+AS $function$
+DECLARE
+ v_description text;
+BEGIN
+ SELECT p.description
+ INTO v_description
+ FROM public.party AS p
+ WHERE p.party_id = p_party_id;
+
+ RETURN v_description;
+END;
+$function$;
+
+COMMENT ON FUNCTION public.get_party_description(bigint) IS
+ 'Returns description for the given party_id; NULL if the row does not exist.';
diff --git a/liquibase/changelog/changes/001-get-party-description.xml b/liquibase/changelog/changes/001-get-party-description.xml
new file mode 100644
index 0000000..ca15a28
--- /dev/null
+++ b/liquibase/changelog/changes/001-get-party-description.xml
@@ -0,0 +1,24 @@
+
+
+
+
+ Read-only PL/pgSQL function get_party_description(party_id)
+
+
+
+
+
+
+
+
+
diff --git a/liquibase/changelog/db.changelog-master.xml b/liquibase/changelog/db.changelog-master.xml
new file mode 100644
index 0000000..eeea4ee
--- /dev/null
+++ b/liquibase/changelog/db.changelog-master.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
diff --git a/liquibase/liquibase.properties b/liquibase/liquibase.properties
new file mode 100644
index 0000000..28ef77e
--- /dev/null
+++ b/liquibase/liquibase.properties
@@ -0,0 +1,13 @@
+# Copy to liquibase.local.properties and override for your environment.
+# Liquibase loads liquibase.local.properties automatically when present.
+
+changeLogFile=changelog/db.changelog-master.xml
+
+url=jdbc:postgresql://localhost:5432/ctl
+username=postgres
+password=
+
+driver=org.postgresql.Driver
+classpath=lib/postgresql.jar
+
+logLevel=info
diff --git a/liquibase/liquibase.properties.example b/liquibase/liquibase.properties.example
new file mode 100644
index 0000000..1938c68
--- /dev/null
+++ b/liquibase/liquibase.properties.example
@@ -0,0 +1,7 @@
+changeLogFile=changelog/db.changelog-master.xml
+
+url=jdbc:postgresql://localhost:5432/your_database
+username=your_user
+password=your_password
+
+driver=org.postgresql.Driver