C Advanced API

Domain builders

Declare a domain with a loading-time predicate installer through the C Advanced API.

Use a domain installer when the predicate vocabulary is assembled by C code rather than kept in a fixed array. For a fixed vocabulary, use stable registration; both paths register a domain by name before loading a policy.

Types

Defined on this page

TypePurpose
maelys_datalog_domain_builder_tOpaque loading candidate passed to an installer.
maelys_datalog_domain_installer_tCallback that adds predicate declarations.

Used from other pages

These types are defined on the linked pages; this page uses them in the roles below.

TypeUse on this pageDefined in
maelys_datalog_domain_tInput domain declaration for advanced registration.Stable Registries
maelys_datalog_predicate_tInput predicate declaration supplied by the installer.Stable Registries

Functions

FunctionPurpose
maelys_datalog_domain_register_advanced()Register a domain with a fixed table or an installer.
maelys_datalog_domain_builder_add()Add one predicate while the installer runs.

Build a domain at policy load

Registration records the domain name and callback; it does not run the callback. Loading a policy creates a bounded candidate registry and invokes the installer. The candidate is published only if every declaration succeeds. Even if the callback ignores a failed domain_builder_add(), the failure remains sticky and loading fails.

C · opaque
maelys_datalog_domain_builder_t
typedef struct maelys_datalog_domain_builder maelys_datalog_domain_builder_t;
Loading-time candidate registry; only the installer receives it.
C · type
maelys_datalog_domain_installer_t
typedef maelys_datalog_status_t (*maelys_datalog_domain_installer_t)(maelys_datalog_domain_builder_t *);
Callback that supplies predicate declarations for one policy load.
C · function
Register an advanced domain
maelys_datalog_status_t
maelys_datalog_domain_register_advanced(
const maelys_datalog_domain_t *domain,
const char *description,
maelys_datalog_domain_installer_t installer
);
Registers a domain with an optional description and a loading-time predicate installer, as an alternative to a fixed predicate table.
Arguments
domainconst maelys_datalog_domain_t *
Domain name, optional policy-source atoms, and either a fixed predicate table or an empty table for an installer.
descriptionconst char *
Optional copied domain description; NULL when unused.
installermaelys_datalog_domain_installer_t
Loading-time callback, or NULL when domain.predicates supplies the table.
Return value
maelys_datalog_status_t

OK on registration; an incompatible registration or invalid combination is rejected. The callback has not run yet.

C · function
Add a predicate to the loading candidate
maelys_datalog_status_t
maelys_datalog_domain_builder_add(
maelys_datalog_domain_builder_t *builder,
const maelys_datalog_predicate_t *predicate
);
Adds one predicate declaration to the candidate registry being built by a domain installer during policy loading.
Arguments
buildermaelys_datalog_domain_builder_t *
Candidate supplied to the installer during loading.
predicateconst maelys_datalog_predicate_t *
One name, arity and origin/query flag declaration; copied into the candidate.
Return value
maelys_datalog_status_t

OK when the declaration is accepted; otherwise loading discards the entire candidate.

Example — document access

The shared stable example uses a fixed array. This alternative registers the same predicate declarations through an installer, without changing the policy or request facts:

CODE
static const maelys_datalog_predicate_t document_predicates[] = {
    {"user", 1u, MAELYS_DATALOG_PREDICATE_EDB},
    {"owns", 2u, MAELYS_DATALOG_PREDICATE_EDB},
    {"delegated", 2u, MAELYS_DATALOG_PREDICATE_EDB},
    {"blocked", 1u, MAELYS_DATALOG_PREDICATE_EDB},
    {"can_read", 2u, MAELYS_DATALOG_PREDICATE_IDB},
    {"has_any_document", 1u, MAELYS_DATALOG_PREDICATE_IDB | MAELYS_DATALOG_PREDICATE_QUERY},
    {"allow", 2u, MAELYS_DATALOG_PREDICATE_IDB | MAELYS_DATALOG_PREDICATE_QUERY},
};

static maelys_datalog_status_t install_documents(maelys_datalog_domain_builder_t *builder) {
    for (size_t i = 0; i < sizeof(document_predicates) / sizeof(document_predicates[0]); ++i) {
        maelys_datalog_status_t rc = maelys_datalog_domain_builder_add(builder, &document_predicates[i]);
        if (rc != MAELYS_DATALOG_STATUS_OK) return rc;
    }
    return MAELYS_DATALOG_STATUS_OK;
}

static maelys_datalog_status_t register_documents(void) {
    const maelys_datalog_domain_t domain = {"documents", NULL, 0u, NULL, 0u};
    return maelys_datalog_domain_register_advanced(
        &domain, "Document access", install_documents);
}

Exactly one of domain.predicates and installer must provide declarations. Policy-source atoms, if any, remain in domain.atoms; the callback does not replace that list. Here blocked("mallory") is a request fact, so no source atom is needed. Domains remain process-global; a context does not isolate them. Registration copies declaration text, but the installer’s executable code must stay available while policies may be loaded.