Перейти к содержанию

Provisa

Подключайте свои базы данных. Делайте запросы через GraphQL, gRPC, SQL или MCP — по любому API или протоколу — за 5 минут.

Provisa обслуживает каждую поверхность API (REST, GraphQL, SQL, gRPC, MCP и другие) над объединённым результатом ваших источников. Это возможно потому, что Provisa — это активный семантический слой: единое определение вашего массива данных — каждый домен, связь и политика по всем источникам, за исключением только самих систем-источников, — которое одновременно и оперирует массивом данных, и обеспечивает его governance. Определение — это не документация, к которой обращается движок; это и есть движок. Зарегистрированные домены и связи — единственные легитимные пути соединения (join), а политики доступа компилируются в каждый план запроса. Одна модель, три задачи:

  • Определение (Define) — Домены, колонки и связи объявляются один раз. Это объявление — та схема, которую видит каждый потребитель, и единственный набор путей соединения, доступный любому запросу.
  • Применение (Enforce) — Безопасность на уровне строк, маскирование колонок, видимость колонок и утверждение запросов применяются встроенно, прямо на пути выполнения. Ни один запрос не достигает данных, минуя их, поэтому покрытие полное по построению, а не благодаря чьей-то усердности.
  • Аудит (Audit) — Поскольку каждый запрос проходит один и тот же управляемый путь, кто запросил что, под какой ролью и по какой политике фиксируется единообразно. Распределённые трассировки, метрики и журналы сами регистрируются как запрашиваемые таблицы наряду с вашими бизнес-данными.

Одно управляемое ядро обслуживает каждый язык и транспорт. Делайте запросы на GraphQL, Cypher или SQL; получайте данные через pgwire, Bolt, gRPC, REST, Arrow Flight или JDBC. Каждый язык запросов понижается до единого промежуточного представления (IR), в которое governance внедряется один раз — поэтому политика не может разойтись между языками, — и это IR перенаправляется в нативный диалект каждого источника на выходе. Добавление языка — это новый фронтенд поверх общего ядра, а не новый движок.

Массив данных одновременно аналитический и транзакционный. Чтение из нескольких источников веерно расходится через слой федерации; запись и чтение из одного источника маршрутизируются напрямую к драйверу источника — управляются идентично, но транзакционно и с задержкой менее 100 мс. Колоночная потоковая передача Arrow Flight встроена изначально.

Вся модель построена из горстки примитивов — доменов, связей, ролей и политик. Небольшой словарь, поэтому определение легко понять и просто вычислять и аудировать: вы можете прочитать набор политик и понять, что он делает. Provisa — это лёгкий компилятор запросов, а не среда выполнения, стоящая на пути данных. Он преобразует запрос в нативные запросы, маршрутизирует их и уходит с дороги — именно поэтому массив данных производителен.

Такая конструкция поддерживает два способа использования, и они не исключают друг друга:

  • Как каркас для модернизации — Смоделируйте свой массив данных, дайте Provisa сгенерировать нативный SQL для каждого источника, затем захватите этот SQL и примените его напрямую в целевой системе. Provisa — это переходный слой, а не постоянная зависимость.
  • Как постоянная инфраструктура применения политик — Оставьте её на месте как управляемый путь, по которому проходит каждый запрос, чтобы определение, применение и аудит оставались едиными на всё время существования массива данных.

Модель федерации

Вся модель сводится к двум контрактам и двум политикам: источники сводятся к двумерным таблицам над одной системой типов, запросы сводятся к одному SQL-подобному IR, достижимость решает, что запрашивается вживую, а что материализуется, а стратегия свежести управляет каждой материализованной копией и производным набором данных. Форма данных на входе, форма запроса на входе, governance на соединении, нативные запросы на выходе. Остальная часть этого раздела разбирает каждую часть подробно.

Модель опирается на одну редукцию: каждый источник выражается как коллекция двумерных таблиц над единой обобщённой системой типов. Это контракт, которому источник должен соответствовать, чтобы присоединиться к массиву данных, и он одинаков для всех источников. Некоторые источники уже подходят — таблица MySQL или PostgreSQL и есть типизированное 2-мерное отношение. Некоторые подходят после проекции: результат GraphQL-запроса, после уплощения, становится таблицей. Некоторые чужеродны этой форме — хранилища триплетов SPARQL, Neo4j — но остаются пригодными для работы, потому что пользователь предоставляет запрос, результирующий набор которого табличен; сам запрос и есть адаптер. Каким бы ни был источник, массив данных видит строки, колонки и обобщённые типы — и ничего больше. Подключение нового вида источника — это соответствие тому единственному контракту, иногда с шагом ручного вмешательства, а не написание индивидуальной интеграции.

У этой редукции есть двойник на стороне запросов. SQL — во всём многообразии диалектов и особенностей — по сути является языком анализа над 2-мерными наборами данных, что делает SQL-подобную форму естественной универсальной целью для запросов. Поэтому каждый запрос, на каком бы языке он ни поступил, самым первым шагом понижается до этого промежуточного представления. Некоторые понижаются гладко — сам SQL и даже GraphQL; некоторые даются тяжело — путевая и графовая семантика Cypher требует настоящей работы, — но всё это выполнимо. Направление каждого запроса в один IR прежде, чем произойдёт что-либо ещё, — вот что позволяет применять governance ровно в одном месте, к одной форме, независимо от того, на каком языке запрос поступил.

Поверх этих двух единообразных форм — табличных источников и единой формы запроса — федерация здесь означает и живой запрос, и хранилище (warehousing) — тот же диапазон, который покрывает движок живых запросов вроде Trino, плюс материализацию, на которую такие движки опираются. Понятие, объединяющее их, — достижимость (reachability): может ли движок для данного источника запросить его на месте, или его данные сначала должны быть материализованы куда-то, где их можно запрашивать? Достижимость разделяет массив данных на то, что запрашивается вживую, и то, что сначала копируется.

Большинство баз данных уже несут некоторое понятие живой связи — ATTACH в DuckDB, postgres_fdw в PostgreSQL, внешние связи Databricks. Так что большинство баз данных могут в какой-то степени выступать движком федерации. Ни одна не является исчерпывающей: каждая достигает определённого набора источников и материализует остальное, без единого описания того, что есть что. Модель закрывает этот пробел, делая достижимость явной — определённый набор методов для каждого источника, указывающий, что движок может достичь вживую, и, соответственно, что должно быть материализовано.

Остаётся вопрос свежести: для каждого недостижимого источника — насколько актуальной должна быть его материализованная копия? На практике это сводится к небольшому набору стратегий — по требованию, по расписанию, по сигналу изменения (CDC, водяной знак, снимок) или закреплённая (pinned). Выбор одной стратегии на источник — это и есть вся политика свежести.

Аналитические наборы данных — производные таблицы, агрегаты, выходы преобразования — укладываются в ту же форму. Они тоже должны быть выражены в IR, и именно поэтому происхождение данных (lineage) не является отдельной системой, которую нужно поддерживать: путь от каждой системы-источника к конечному выходу и есть тот IR, который его произвёл, читаемый от начала до конца. Построение таких наборов поднимает вопрос свежести на шаг дальше — обновляется ли набор данных по расписанию, только после выполнения его предусловий, непрерывно в режиме, близком к реальному времени, или как закреплённый исторический снимок? Способы выразить, как и когда строить набор данных, — тот же небольшой перечислимый набор, поэтому производный набор данных несёт политику построения в точности в том же словаре, что и копия источника.

Размерные модели — прямое применение этого. Таблицы фактов и измерений схемы «звезда» — такие же аналитические наборы данных, как и любые другие: измерение — это согласованная, дедуплицированная проекция; таблица фактов — это join и агрегация, сведённые к грануле, — каждая со своей политикой построения и свежести. Медленно меняющиеся измерения (slowly changing dimensions) не требуют специального механизма: закреплённый снимок — это история Типа 2, перестроение по расписанию — Тип 1. И поскольку схема определена в IR, а не физически привязана к таблицам одного хранилища, те же определения фактов и измерений перенацеливаются — материализуются в Oracle, в Databricks или остаются виртуальными поверх MPP-движка — без переделки модели. Модель генерирует схему «звезда»; она не привязывает её к конкретному движку.

Data Vault укладывается таким же образом, на слой раньше. Его хабы — это дедуплицированные наборы данных по бизнес-ключу, его связи (links) — зарегистрированные связи между ними, а его сателлиты — это наборы данных только для вставки, с временными метками — исторический учёт. Сателлит — это просто производный набор данных по стратегии свежести на основе сигнала изменения: дата загрузки плюс hashdiff — это CDC, применённый к описательным атрибутам, а история только для вставки — это стратегия закреплённого снимка. Таблицы «точка во времени» (point-in-time) и мостовые таблицы — это дальнейшие производные наборы данных, построенные для производительности запросов. Так что сырой vault — это набор аналитических наборов данных в IR, а схема «звезда» — проекция от него, — и то, и другое генерируется и переносимо между движками. Чего модель не делает — так это не принимает решение о методологии: что становится хабом, какая гранула у сателлита, стратегия разделения. Это остаётся выбором моделирования; будучи сделанным, он живёт как переносимый IR, а не как ETL, приваренный к одному хранилищу.

Оба паттерна объявляются через два первоклассных сокращения вместо написанных вручную представлений — примитивы, из которых построены любая схема «звезда» и Data Vault, сохраняющие нейтральность к методологии:

  • entity — ключевая, дедуплицированная, опционально историзируемая проекция источника. Объявите ключ сущности, атрибуты и режим истории; Provisa понижает это до материализованного представления, а при запросе истории — до битемпорального MV (scd2 → дельта, snapshot → снимок). Одна конструкция обслуживает и Kimball-измерение (SCD1/SCD2), и хаб + сателлит Data Vault.
  • fact — join к ключам сущностей, сведённый к объявленной грануле, с агрегированными мерами. Provisa понижает это до агрегатного MV плюс зарегистрированные связи с сущностями. Одна конструкция обслуживает и таблицу фактов схемы «звезда», и link Data Vault (факт без мер — это чистая связь набора ключей).

Поскольку понижение чистое — спецификация entity/fact становится ровно теми определениями MV, битемпоральности и связей, которые моделист иначе писал бы вручную, — хранилище представляет собой IR до самого низа и перенацеливается между движками без переделки модели. Объявите хранилище в admin UI (форма Model для сущностей и фактов) или через admin API (registerEntity / registerFact); модель генерирует звезду Kimball или Data Vault, а не навязывает какую-то одну из них.

Путешествие во времени

Путешествие во времени — простая идея: сохранять каждую версию строки вместо того, чтобы перезаписывать её, чтобы можно было спросить, какими были данные в любой прошлый момент. Что различается, так это то, насколько эффективно каждый движок может это делать, и именно поэтому Provisa делает это свойством определения материализованного представления, а не свойством движка хранения (REQ-1162). Объявите один раз — работает на любом материализующем бэкенде.

Правило, которое сохраняет переносимость, — только добавление (append-only): версия, однажды записанная, никогда не обновляется и не удаляется. Отправка строки «на пенсию» путём записи обратно даты «действительно до» — обычный битемпоральный трюк — требует UPDATE, а многие движки не умеют делать это дёшево (или вообще) над федеративным хранилищем, поэтому Provisa так не делает. Вместо этого каждое обновление добавляет, а «какая версия действовала в момент времени T» выводится во время чтения из неизменяемого журнала. Есть ровно два способа добавления:

  • Snapshot (снимок) — добавить весь свежий набор данных целиком, с меткой системного времени этого обновления. Без сравнения разниц; корректно на любом движке; хранилище растёт на полную копию с каждым обновлением.
  • Delta (дельта) — добавить только то, что изменилось, плюс надгробия (tombstones) для удалённых ключей. Дельта вычисляется движком (анти-join внутри INSERT … SELECT), а не сворачивается построчно в Provisa. Меньше по объёму, и требует ключа сущности.

Системное время (когда Provisa зафиксировала версию) управляется таким образом; действительное время (когда факт истинен в бизнесе) поставляется собственным SELECT представления и сохраняется. Движки, предлагающие больше — нативные снимки Iceberg, MERGE, поддерживающий меньше строк, — могут использоваться для эффективности за тем же объявлением; путь только-добавления — это тот минимум, который корректен везде.

Чтение прозрачно. Обычный запрос к битемпоральному MV по умолчанию восстанавливает текущее состояние из журнала добавлений; чтобы отправиться во времени, отправьте заголовок X-Provisa-As-Of: <timestamp>, и весь запрос будет отвечен так, как выглядел массив данных в тот момент, — идентичная семантика на любом субстрате. Включите это для любого материализованного представления в admin UI (элемент управления Time Travel: выкл / snapshot / delta плюс ключ сущности) или через admin API.

Достижимость плюс свежесть — это общая модель для федерации данных: определение, которое говорит, что живо, что материализовано и насколько свежей остаётся каждая копия, — независимо от досягаемости какого-либо одного движка. Итог — свобода от привязки к проприетарному вендору. Модель переносима; массив данных не является заложником того, чья федерация сегодня случайно достигает наибольшего числа источников.

Возможности

Интерфейсы запросов

Это языки и структурированные API, на которых вы пишете запросы. У каждого свой синтаксис и своя семантика; governance (RLS, маскирование, видимость колонок, применение связей) действует единообразно для всех из них независимо от того, какой проводной протокол их доставляет.

  • GraphQL — Схемы для каждой роли с видимостью на уровне полей, фильтрацией, курсорной пагинацией и агрегатными запросами (count, sum, avg, min, max). Ограничена схемой до зарегистрированных связей — структурно корректна по построению, самый быстрый путь к правильному простому запросу. Включена Apollo APQ: запросы хешируются и регистрируются на стороне сервера; последующие вызовы отправляют только хеш через HTTP GET, что делает ответы кешируемыми в CDN без каких-либо изменений на стороне клиента. Справочные таблицы ниже настраиваемого порога строк предоставляются как типы enum.
  • SQL — Полный SQL над федеративными данными; без ограничений и выразительнее, чем GraphQL. Пишите стандартный SQL — с коррелированными подзапросами и всем прочим, — и он выполняется по источникам без изменений. Запросы к одному источнику полностью минуют слой федерации (менее 100 мс).
  • Cypher — Язык графовых запросов над той же федеративной схемой. Обходите связи как рёбра графа; объединяйте источники; используйте пути переменной длины. Governance применяется идентично GraphQL и SQL.
  • gRPC model API — Автоматически сгенерированный .proto из зарегистрированной схемы; типизированные RPC для запроса и вставки на каждую таблицу, потоковые ответы. Управляется схемой в том же смысле, что и GraphQL — модель регистрации является контрактом, protobuf — проводной кодировкой. В отличие от Arrow Flight (являющегося колоночным потоковым транспортом), это полноценный интерфейс запросов для каждой таблицы.
  • JSON:API — Структурированный API запросов по адресу /data/jsonapi/{table}, изначально только для HTTP. Поддерживает JSON:API 1.1: разреженные наборы полей (fields[table]=col1,col2), выражения фильтров (filter[field][op]=value), составные документы (include=relation) и сортировку. Не является языком запросов общего назначения — запрашивает по одной таблице за раз со стандартизированным синтаксисом фильтров, а не произвольной строкой запроса.
  • Query Language Explorer — Напишите GraphQL-запрос и увидите живые переводы в Semantic SQL и Cypher в боковых панелях; скопируйте любой из них или перейдите напрямую в редактор SQL или Graph. Практичный рабочий процесс — набросать фрагменты запроса в GraphQL, затем сшить получившийся SQL в сложные представления или отчёты.

Explorer показывает GraphQL-запрос рядом с его живыми переводами в SQL и Cypher:

Query Language Explorer

Ту же федеративную схему можно исследовать как живой граф — метки доменов и узлов, типы связей и обходы переменной длины:

Graph Visualization

Инструменты составления запросов

Эти инструменты помогают писать запросы на указанных выше языках — сами по себе они не являются языками запросов.

  • Запросы на естественном языке — Конвейер NL→SQL/Cypher/GraphQL на базе Claude. Опишите, что вам нужно, простым языком; конвейер производит запрос на выбранном вами языке с интерактивным циклом проверки перед выполнением.

Natural Language Query

Проводные протоколы

Это протоколы соединения. SQL, GraphQL и Cypher передаются через них — выбор проводного протокола не меняет интерфейс запросов или поведение governance.

  • pgwire — Любой клиент PostgreSQL (psql, DBeaver, DataGrip, asyncpg, SQLAlchemy, pandas read_sql) подключается на порт 5439, как будто это сервер Postgres. Принимает только SQL. Применяется полный конвейер governance. pg_catalog и information_schema обслуживаются из каталога в памяти, так что браузеры схем работают без обращения к федерации. TLS опционален.
  • Bolt (Neo4j) — Любой клиент Neo4j (Neo4j Browser, Bloom, официальные драйверы) подключается по протоколу Bolt и выполняет Cypher над федеративным графом. Каждая роль, которой обладает пользователь, отображается как база данных provisa_<role>. Такой же governance, как и на любом другом транспорте. TLS опционален.
  • Arrow Flight — Высокопроизводительная колоночная потоковая передача через gRPC; принимает GraphQL или SQL как входной запрос. Неограниченные результирующие наборы, без материализации на стороне сервера, без отдельной инфраструктуры.
  • JDBC — Интеграция BI-инструментов (Tableau, Power BI, DBeaver) в режиме approved или catalog.
  • WebSocket / SSE — Подписки: события изменений почти в реальном времени; бэкенды: PG native, MongoDB native, CDC, опрос (polling). Также доступно через Kafka.

Источники данных

  • 53 типа источников — PostgreSQL, MySQL, MongoDB, Cassandra, Elasticsearch, Neo4j, хранилища триплетов SPARQL, Kafka, Google Sheets и другие через единый API; графовые и RDF-источники являются полноправными, а не адаптерами
  • Умная маршрутизация — Запросы к одному источнику минуют федерацию (менее 100 мс); запросы к нескольким источникам маршрутизируются через слой федерации — используйте собственный кластер или встроенных воркеров
  • API-источники — Регистрируйте конечные точки REST, GraphQL, gRPC, WebSocket или RSS как запрашиваемые таблицы; включены вспомогательные средства для SPARQL; федеративные join между API-источниками и реляционными источниками работают прозрачно
  • Интроспекция удалённой схемы — Укажите на любую конечную точку GraphQL, OpenAPI или gRPC; документированные операции автоматически предоставляются как запрашиваемые таблицы, узлы и рёбра графа с полностью применённым governance
  • Файловые источники — Файлы CSV, Parquet и SQLite как запрашиваемые таблицы; поддерживает локальные пути и удалённое объектное хранилище (s3://, ftp://, sftp://)
  • Интеграция с Kafka — Темы как таблицы только для чтения; результаты запросов как приёмники Kafka
  • Запланированные триггеры — Cron- и интервальные триггеры (APScheduler), запускающие webhook'и, мутации или публикации в приёмники Kafka
  • Подсказки производительности федерации — Подсказки маршрутизации в SQL-комментариях переопределяют автоматические решения о маршрутизации

Data Sources

Источники, файлы и удалённые конечные точки регистрируются как управляемые таблицы прямо из UI:

Table Registration

Безопасность и governance

  • Безопасность на уровне строк — Внедрение WHERE-условия для каждой таблицы, для каждой роли
  • Маскирование колонок — Маскирование для каждой колонки (regex, константа, усечение) с обходом на основе роли
  • Пресеты колонок — Статические или основанные на переменных сессии значения, внедряемые на стороне сервера при insert/update; не раскрываются во входных типах мутаций
  • Права на запись — Контроль доступа на мутацию для каждой колонки (writable_by)
  • Наследуемые роли — Роли рекурсивно наследуют RLS, видимость и маскирование от родительской роли
  • Отслеживаемые функции и webhook'и — Функции БД и исходящие webhook'и, предоставленные как мутации GraphQL с типизированными формами возврата
  • Хук утверждения ABAC — Хук авторизации перед выполнением; транспорт webhook, gRPC или unix_socket; область действия для таблицы, источника или глобальная; настраиваемая политика отката (fallback)
  • Подключаемая аутентификация — Firebase, Keycloak, OAuth 2.0, simple (для тестирования)

Security Roles

Доставка и производительность

  • Материализованные представления как записанные преобразования — MV фиксирует преобразование, которое его произвело: его форму join или SQL, входные сигналы для каждого источника (снимок Iceberg, водяной знак RDB), из которых оно было построено, и проверку детерминированности при регистрации. Поскольку преобразование записано, запросы (или подвыражения) прозрачно переписываются на свежий MV — структурное сопоставление паттернов join с поддержкой частичного совпадения, так что MV, покрывающий подмножество join'ов, всё равно применяется, а оставшиеся join сохраняются
  • Встраивание горячих таблиц — Небольшие часто соединяемые справочные таблицы встраиваются как VALUES CTE прямо в план запроса, устраняя обращения между источниками для данных измерений
  • Кеширование запросов — Кеш результатов Redis, разделённый по роли+RLS; включён кеш хешей APQ
  • Наблюдаемость как данные — Распределённые трассировки, метрики и журналы собираются через OpenTelemetry, уплотняются в Iceberg на S3 и автоматически регистрируются как запрашиваемые таблицы (traces, metrics, logs, queries) в федеративной схеме; запрашивайте их через SQL, GraphQL или Cypher наравне с вашими бизнес-данными — соедините таблицу customers с таблицей queries, чтобы увидеть, кто что запускал и сколько это заняло

Администрирование и интеграция

  • Admin API — GraphQL по адресу /admin/graphql; загрузка/выгрузка конфигурации, редактирование связей, утверждение запросов
  • Просмотрщик отчётов/admin/reports перечисляет встроенные управляющие представления домена ops и любые зарегистрированные пользовательские отчёты; требует возможность observability
  • Предпросмотр таблицы — у каждой зарегистрированной таблицы есть управляемый просмотрщик данных с постраничной выборкой на сервере, проталкиваемыми вниз фильтрами, многоуровневой группировкой и экспортом в CSV
  • GraphQL Voyager — Интерактивная визуализация схемы в области видимости роли как диаграммы «сущность-связь»
  • LLM-обнаружение связей — Предложения кандидатов на внешний ключ на базе Claude
  • Python-клиентpip install provisa-client; GraphQL/SQL → DataFrame, Arrow Flight → таблицы pyarrow, диалект SQLAlchemy, поддержка ADBC
  • Приём данных — HTTP-конечные точки для отправки данных о событиях в формате JSON в платформу
  • Импорт Hasura v2 / DDN — Преобразование метаданных Hasura v2 или YAML supergraph DDN в конфигурацию Provisa
  • Apollo Federation — Предоставление Provisa как субграфа Apollo Federation v2

Схема в области видимости роли, визуализированная как диаграмма «сущность-связь» (GraphQL Voyager):

Schema Voyager

Связи регистрируются, утверждаются и применяются как единственные легитимные пути JOIN:

Relationships

Модель безопасности

Именно здесь фраза «на пути, которым и так проходит каждый запрос» перестаёт быть лозунгом. Provisa применяет многослойную модель безопасности по каждому языку запросов (GraphQL, SQL, Cypher) и каждому транспорту (REST, gRPC, Arrow Flight, JDBC, pgwire, Bolt, WebSocket). Governance применяется единообразно — не существует пути запроса, который бы его обходил. Покрытие полное по построению, а не благодаря усердности: добавьте источник, колонку или связь, и каждый слой применится к ним автоматически, без необходимости что-либо регистрировать вручную.

Слои применяются по порядку. Запрос должен пройти каждый слой, прежде чем будет оценён следующий.

Слой 0 — Фильтрация интроспекции

Схема и каталог, предоставляемые роли, содержат только таблицы из её списка domain_access и колонки, прошедшие проверку по правилам visible_to для каждой колонки. Объекты вне доступа роли невидимы уже на этапе обнаружения — их нельзя запросить, автодополнить или вывести их существование. Это касается и схемы GraphQL, и каталога SQL, и браузера схемы в редакторе запросов.

Слой 1 — Публичный доступ

Таблицы в доменах без ограничения domain_access видны всем аутентифицированным идентичностям без дополнительной настройки. Никакого трения для по-настоящему публичных данных.

Слой 2 — Доступ к домену

Каждая роль несёт список domain_access из идентификаторов доменов. Запрос, затрагивающий таблицу вне этих доменов, отклоняется до выполнения. Это грубая граница владения — роль HR не может достичь таблиц финансов независимо от того, как написан SQL.

Слой 3 — Безопасность на уровне строк

После подтверждения доступа к домену для каждой таблицы, для каждой роли предикаты WHERE внедряются в каждый SELECT во время выполнения. Предикаты вычисляются над сырыми данными. Региональный менеджер, запрашивающий общую таблицу orders, видит только строки своего региона даже при SELECT *.

Слой 4 — Видимость и маскирование колонок

Колонки со списком visible_to, исключающим запрашивающую роль, удаляются из вывода запроса. Колонки с правилом маскирования получают заменённые значения — редактирование по regex, замена константой или усечение — до того, как результаты покидают сервер. Маскирование применяется во всех языках запросов и форматах вывода.

Слой 5 — Защита предикатов

Замаскированные колонки отклоняются в предложениях WHERE и HAVING. Без этого вызывающая сторона могла бы вывести незамаскированное значение путём бинарного поиска в фильтре, даже если вывод замаскирован. Отклонение применяется на этапе разбора запроса, до выполнения.

Governance связей

Условия JOIN в SQL должны соответствовать зарегистрированной, утверждённой связи между таблицами. Неутверждённые join отклоняются. Каждая связь несёт понятную человеку причину и описание — руководство и для пользователей, и для автономных агентов о том, зачем существует путь обхода. Это политика governance, а не жёсткая граница безопасности: слои 2–5 действуют независимо от структуры join, поэтому намеренный обход не раскрывает данные, которые роль не смогла бы получить через два отдельных запроса. Попытки обхода журналируются и доступны для аудита.


Эти слои складываются. Роль с доступом к домену, RLS и замаскированными колонками имеет все пять ограничений активными одновременно. Добавление нового источника данных, колонки или связи не требует обновления каждого правила — каждый слой настраивается независимо и применяется автоматически к любому запросу, затрагивающему управляемые объекты.

macOS

  1. Скачайте Provisa-macOS.dmg (всегда последний релиз)
  2. Перетащите Provisa.app в /Applications и дважды щёлкните для запуска
  3. Первый запуск выполняет одноразовую настройку (~2 мин, интернет не требуется)
  4. Откройте терминал:
provisa start   # start all services
provisa open    # open the UI in your browser

Linux

  1. Скачайте Provisa-linux-x86_64.AppImage (всегда последний релиз)
  2. Сделайте его исполняемым и запустите — первый запуск выполняет одноразовую настройку (интернет не требуется):
chmod +x Provisa-*-linux-x86_64.AppImage
./Provisa-*-linux-x86_64.AppImage
provisa start && provisa open

Windows

  1. Скачайте Provisa-windows-x64.exe (всегда последний релиз)
  2. Запустите установщик — права администратора не требуются
  3. Откройте Provisa First Launch из меню Пуск — выполняется одноразовая настройка (~5 мин, интернет не требуется)
  4. Откройте новый терминал:
provisa start

Первый запрос

В локальной разработке (PROVISA_MODE=test) учётные данные не требуются. В продакшене аутентифицируйтесь Bearer-токеном — роль извлекается из него автоматически.

# Local dev — no auth required, role defaults to admin
curl -X POST http://localhost:8001/data/graphql \
  -H "Content-Type: application/json" \
  -d '{"query": "{ orders { id amount region } }"}'

# Ad-hoc SQL works the same way
curl -X POST http://localhost:8001/data/graphql \
  -H "Content-Type: application/json" \
  -d '{"query": "SELECT id, amount, region FROM orders"}'

# Production — authenticate with a Bearer token; role is derived from the token
curl -X POST https://provisa.example.com/data/graphql \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"query": "{ orders { id amount region } }"}'

JDBC (Tableau, DBeaver, Power BI)

Скачайте provisa-jdbc.jar (всегда последний релиз) и добавьте его в путь драйверов вашего BI-инструмента.

jdbc:provisa://localhost:8815

Аутентифицируйтесь с помощью вашего имени пользователя и пароля Provisa — сервер назначает вашу роль.

  • Режим catalog — видна полная схема; используйте с каталожными инструментами (Collibra, Atlan, DBeaver)

Шаги настройки для Tableau и Power BI см. в docs/integrations.md.

Проводной протокол PostgreSQL (pgwire)

Provisa говорит на проводном протоколе PostgreSQL на порту 5439. Любой клиент, умеющий подключаться к Postgres, подключается к Provisa — без драйвера, без адаптера, без изменений в существующей оснастке.

Имя пользователя PostgreSQL выбирает роль Provisa. С provider: none (режим доверия) пароль игнорируется, и любое настроенное имя роли принимается как имя пользователя — подключайтесь как analyst, admin или любая другая роль, чтобы увидеть управляемое представление данных этой роли. С provider: simple пароль проверяется через bcrypt. Другие провайдеры (firebase, keycloak, oauth) не поддерживаются через pgwire.

# psql — connect as analyst role
psql -h localhost -p 5439 -U analyst

# psql — connect as admin role
psql -h localhost -p 5439 -U admin

# asyncpg (Python) — role = username, password ignored in trust mode
conn = await asyncpg.connect(host="localhost", port=5439, user="analyst", password="x")
rows = await conn.fetch("SELECT id, amount FROM orders WHERE region = 'west'")

# SQLAlchemy
engine = create_engine("postgresql+psycopg2://analyst:x@localhost:5439/provisa")

# pandas
df = pd.read_sql("SELECT * FROM orders", engine)

Все запросы проходят через полный конвейер governance — доступ к домену, RLS, маскирование и защита предикатов применяются точно так же, как для GraphQL и REST. Браузеры схем (DBeaver, DataGrip, pgAdmin) работают из коробки: запросы к pg_catalog и information_schema обслуживаются из каталога в памяти, ограниченного доступом роли к доменам, так что пользователи видят только те таблицы и колонки, которые им разрешено запрашивать.

DataGrip просматривает управляемую схему и её диаграмму внешних ключей через pgwire — без драйвера, без адаптера:

Provisa in DataGrip over pgwire

TLS включается установкой PROVISA_PGWIRE_CERT и PROVISA_PGWIRE_KEY. Порт настраивается через PROVISA_PGWIRE_PORT (по умолчанию 5439).

Bolt (проводной протокол Neo4j)

Provisa также говорит на протоколе Bolt Neo4j, поэтому графо-нативные инструменты подключаются напрямую и выполняют Cypher над федеративным графом — без экспорта, без отдельной графовой базы данных. Направьте Neo4j Browser или Bloom на Provisa и обходите связи по источникам с тем же применённым governance (доступ к домену, RLS, маскирование).

Neo4j Browser выполняет Cypher над Provisa — метки узлов, типы связей и ключи свойств приходят прямо из зарегистрированной схемы:

Provisa in Neo4j Browser over Bolt

Включите его, установив PROVISA_BOLT_PORT (значение Neo4j по умолчанию — 7687). TLS включается через PROVISA_BOLT_CERT и PROVISA_BOLT_KEY. Каждая роль Provisa, которой обладает аутентифицированный пользователь, отображается как выбираемая база данных provisa_<role> (селектор provisa_admin выше) — выбор одной сужает сессию до прав домена этой роли; пользователь никогда не может превысить роли, которыми он обладает.

Python-клиент

pip install provisa-client                       # core
pip install "provisa-client[pandas]"             # + DataFrame support
pip install "provisa-client[sqlalchemy]"         # + SQLAlchemy dialect
pip install "provisa-client[adbc]"               # + ADBC over Arrow Flight
from provisa_client import ProvisaClient, connect

# GraphQL → DataFrame
client = ProvisaClient("http://localhost:8001", username="alice", password="secret")
df = client.query_df("{ orders { id amount region } }")

# SQL → DataFrame
df = client.query_df("SELECT id, amount, region FROM orders WHERE region = 'west'")

# Arrow Flight → pyarrow Table (high-throughput columnar)
table = client.flight("{ orders { id amount region } }")

# DB-API 2.0 (PEP 249) — GraphQL or SQL, detected automatically
with connect("http://localhost:8001", username="alice", password="secret") as conn:
    cur = conn.cursor()

    # GraphQL
    cur.execute("{ orders { id amount region } }")
    rows = cur.fetchall()

    # SQL (routed through governance engine — RLS and masking applied)
    cur.execute("SELECT id, amount FROM orders WHERE region = %s", ("west",))
    rows = cur.fetchall()

# SQLAlchemy dialect — provisa+http:// or provisa+https://
from sqlalchemy import create_engine, text
import pandas as pd

engine = create_engine("provisa+http://alice:secret@localhost:8001")

# pandas read_sql — GraphQL or SQL
df = pd.read_sql("{ orders { id amount region } }", engine)
df = pd.read_sql("SELECT id, amount, region FROM orders WHERE region = 'west'", engine)

# raw execute
with engine.connect() as conn:
    rows = conn.execute(text("SELECT id, amount FROM orders")).fetchall()

# role + mode URL parameters (mode=catalog for arbitrary SQL)
engine = create_engine(
    "provisa+http://alice:secret@localhost:8001?role=analyst&mode=catalog"
)

# ADBC — Arrow-native streaming via Flight
from provisa_client.adbc import adbc_connect
with adbc_connect("http://localhost:8001", user="alice", password="secret") as conn:
    with conn.cursor() as cur:
        cur.execute("{ orders { id amount } }")
        table = cur.fetch_arrow_table()

Полное описание см. в docs/python-client.md.

Документация

Тема Документ
Быстрый старт для разработчика (запуск из исходников) docs/quickstart.md
Полный справочник конфигурации YAML docs/configuration.md
Справочник конечных точек (GraphQL, REST, Flight, gRPC) docs/api-reference.md
Устройство системы и карта компонентов docs/architecture.md
Модель безопасности (RLS, маскирование, аутентификация) docs/security.md
Поддерживаемые типы источников docs/sources.md
Подписки SSE docs/subscriptions.md
JDBC, BI-инструменты, клиенты Arrow Flight, Apollo Federation docs/integrations.md
Python-клиент (provisa-client) docs/python-client.md
Admin API docs/admin.md
Развёртывание (Docker Compose, Kubernetes, macOS) docs/deployment.md
Импорт Hasura v2 / DDN docs/import.md
Рабочий процесс релизов (теги alpha/beta/stable) docs/releasing.md

Расчёт размера

Provisa включает встроенный движок федерации для запросов к нескольким источникам. При первом запуске вы выбираете бюджет RAM; Provisa автоматически выводит число локальных воркеров федерации.

RAM хоста Воркеры Типичная нагрузка
< 24 ГБ 0 Разработка, запросы к одному источнику, небольшие команды
24–47 ГБ 1 Небольшая команда, умеренные межисточниковые запросы
48–95 ГБ 2 Развёртывание на уровне отдела, смешанное использование BI + notebook
96 ГБ+ 4 Крупный отдел, интенсивная параллельная федерация

Число воркеров можно изменить в любой момент, отредактировав ~/.provisa/config.yaml (federation_workers: N) и запустив provisa restart. Установите 0, чтобы работать только в режиме координации (одноузловой режим).

Масштабирование за пределами одной машины

Горизонтальное масштабирование — Запускайте несколько экземпляров Provisa за балансировщиком нагрузки. Каждый экземпляр — полностью функционирующая система. Все экземпляры должны указывать на одну и ту же БД конфигурации (установите CONFIG_DB_HOST на вторичных машинах) и опционально на общий экземпляр Redis (REDIS_URL) для единого кеша. Большинство запросов распределяются прозрачно; очень крупные межисточниковые join могут превысить ресурсы одного экземпляра и потребовать более крупную машину или внешний кластер федерации.

Общий Redis — Установите REDIS_URL на каждом экземпляре, чтобы указать на внешний Redis. Общий Redis означает, что записи кеша с одного экземпляра доступны всем, что повышает долю попаданий в кеш по всему кластеру.

Используйте собственный кластер федерации — Направьте Provisa на существующий внешний кластер федерации вместо встроенных воркеров. Рекомендуется для крупномасштабных или облачных развёртываний; конфигурацию см. в docs/deployment.md.

Лицензия

Business Source License 1.1 (без изменений, согласно ковенантам лицензиара MariaDB). Каждая выпущенная версия переходит на Change License (GPL v2.0 или более поздней) на 4-ю годовщину своего публичного релиза; текущий и недавний код остаётся под BSL. Промышленное использование сверх порогов Additional Use Grant (менее 100 сотрудников/подрядчиков и выручка за предыдущий год менее $1 млн) требует коммерческой лицензии. См. LICENSE.

Лицензиар не даёт согласия на использование этой работы для обучения AI/ML. См. NOTICE, ai.txt и robots.txt. По вопросам коммерческих лицензий или лицензий на AI-обучение: kennethstott@gmail.com