aboutsummaryrefslogblamecommitdiffstats
path: root/include/RNT_C_API.h
blob: 707c04aa2941b1087400e63d51810b2ae0837cf9 (plain) (tree)




































                                                                                

                










                                                                        
                                                                        


                                                                        
                                                                        

                                                                    

                                                                      










                                                                            
                                                                  












                                                                               
                                                                    

                                                                        
















                                                                           
                                                                               










                                                                                 
                                                       

















                                                                            


                                                           

                                                                        














                                                                                  
                                                                          




                                                                       
                                                 

   
                                                                       
  



                                                                           
  


                                                                            

                                                                  
                                                                          

   
                                                                          
  



                                                                              





                                                                              





                                                                                 
   
                                                                             










                                                                          

                                                                            

                                           
                                                   

   
                                                                                     

                                                                           







                                                                           

                                           
                                                                          















                                                                            
                                                                      











                                                                                 
                                                                            












                                                                                      
                                                                              


















                                                                               


                                                    












                                                                              
                                                                               










                                                                             
                                                         













                                                                              
                                                                              
 













                                                                              
                                                         












                                                                                 
                                                                  




                                                     
                                                 














                                                                                
                                                                         

                                                                                   
                                                           









                                                                               
                                                                   







                                                                                 
                                                                 







                                                                             
                                           









                                                                                 
                                                         










                                                                                
                                                                        




                                                                            
                                                       





                                                                             
                                     

                                                                                  
                                       



                  
#pragma once

/**
 * @file RNT_C_API.h
 * @brief C-callable surface over the RNT manager pipeline for OCaml ctypes.
 *
 * All types are opaque void pointers on the C side; the implementation casts
 * them to the appropriate C++ types internally. Callers must never dereference
 * handle or cursor pointers directly.
 *
 * ## Memory contract
 *   - Strings returned via out-parameters (char**) are heap-allocated by the
 *     API and must be released with rnt_free_string().
 *   - Payloads returned via (uint8_t**, size_t*) are heap-allocated and must
 *     be released with rnt_free_bytes().
 *   - Handles and cursors are owned by the caller and must be closed before
 *     the program exits.
 *
 * ## Error convention
 *   - Functions returning int use 0 for success and a negative value for error.
 *   - Functions returning a pointer return NULL on failure.
 *
 * ## Thread safety
 *   This API is **not thread-safe**. The global runtime (g_rt) is a single
 *   in-process instance; all callers share the same ObjectManager and storage
 *   backend. The expected usage model is a single OCaml thread (or domain)
 *   driving the API at a time. If concurrent access is needed in the future,
 *   a per-object or per-relation mutex strategy should be introduced.
 *
 * @todo Implement AUTH_CLAIM::READ/WRITE enforcement in rnt_open_handle once
 *       PermissionsManager::Access is wired to a real policy engine. Currently
 *       all handles open with full access regardless of the claims parameter.
 */

#include <stddef.h>
#include <stdint.h>

#include "Api.h"

#ifdef __cplusplus
extern "C" {
#endif

/** Opaque handle to a registry object opened through HandlerManager. */
typedef void* rnt_handle_t;

/** Opaque cursor over a relation, driven by the VM or used directly. */
typedef void* rnt_cursor_t;

/* ------------------------------------------------------------------ */
/* Runtime lifecycle                                                  */
/* ------------------------------------------------------------------ */

/**
 * @brief Initializes the RNT runtime with the selected storage backend.
 *
 * Must be called before any other API function. Once the runtime is
 * successfully initialized, subsequent calls are no-ops and return 0.
 * If initialization fails (returns negative), the call may be retried
 * with corrected parameters — the runtime is left in a clean state.
 *
 * @param driver        Storage driver to use: "sqlite" or "memory".
 *                      "sqlite" persists data to @p storage_path.
 *                      "memory" keeps all data in process memory (ignores
 *                      @p storage_path); intended for tests.
 * @param storage_path  File path for the SQLite database, or ":memory:" for
 *                      an ephemeral SQLite store. Ignored when driver is
 *                      "memory".
 * @return 0 on success, negative on error.
 */
NT_API int rnt_init(const char* driver, const char* storage_path);

/* ------------------------------------------------------------------ */
/* Authentication                                                       */
/* ------------------------------------------------------------------ */

/**
 * @brief Runs PermissionsManager::Firewall for a connection.
 *
 * @param auth_method  "plain_text" or "certificate".
 * @param claims_out   Set to a heap-allocated, newline-separated claim string.
 *                     Release with rnt_free_string(). NULL on error.
 * @return 0 on success, negative when authentication is rejected.
 */
NT_API int rnt_firewall(const char* auth_method, char** claims_out);

/* ------------------------------------------------------------------ */
/* Session lifecycle                                                    */
/* ------------------------------------------------------------------ */

/**
 * @brief Opens a session and registers it at /system/sessions/<hash>.
 *
 * The runtime mints a random 256-bit hex hash for the session; the caller
 * has no influence over the value. The returned string is the opaque
 * identifier required by every other rnt_session_* call.
 *
 * @param connection_context  Caller-owned pointer carried on the session.
 *                            The runtime treats it as opaque; ownership of
 *                            the pointee remains with the caller.
 * @param session_hash_out    Set to a heap-allocated 64-char hex string.
 *                            Release with rnt_free_string().
 * @return 0 on success, negative on error.
 */
NT_API int rnt_session_open(void* connection_context, char** session_hash_out);

/**
 * @brief Closes a session, unregistering it from /system/sessions/<hash>.
 *
 * After this call the session hash is invalid; subsequent rnt_session_* or
 * resolver lookups against it will fail. Branch overrides held by the
 * session are dropped.
 *
 * @param session_hash  Hash returned by rnt_session_open.
 * @return 0 on success, negative when the hash does not match an active session.
 */
NT_API int rnt_session_close(const char* session_hash);

/**
 * @brief Sets a per-session override for a branch.
 *
 * While the override is in effect, paths resolved through
 * /system/sessions/<hash>/branches/<branch_name>/... point at
 * /system/snapshots/<target_hash>/... regardless of where the global branch
 * HEAD currently sits. Pass an empty string for @p target_hash to remove
 * the override and resume falling back to the global branch.
 *
 * Non-empty target hashes must correspond to an existing
 * /system/snapshots/<target_hash> entry; otherwise the call fails.
 *
 * @param session_hash  Session identifier.
 * @param branch_name   Branch the override applies to (e.g. "main").
 * @param target_hash   Snapshot hash to bind, or "" to clear.
 * @return 0 on success, negative on error.
 */
NT_API int rnt_session_set_branch(const char* session_hash,
                                  const char* branch_name,
                                  const char* target_hash);

/* ------------------------------------------------------------------ */
/* Handle lifecycle                                                     */
/* ------------------------------------------------------------------ */

/**
 * @brief Opens a handle to the object at the given slash-separated path.
 *
 * Runs the full HandlerManager::Open pipeline:
 * ObjectManager::Find → PermissionsManager::Access →
 * IdentityManager::CanOpen → LifecycleManager::Contention →
 * LifecycleManager::Monitor.
 *
 * @param path    Slash-separated logical path, e.g. "/system/branches/main".
 * @param claims  Claim string returned by rnt_firewall (may be NULL).
 * @return Handle pointer, or NULL when the path is not found or access is denied.
 */
NT_API rnt_handle_t rnt_open_handle(const char* path, const char* claims);

/**
 * @brief Closes a handle, running HandlerManager::Close and Unmonitor.
 * @return 0 on success, negative on error.
 */
NT_API int rnt_close_handle(rnt_handle_t handle);

/**
 * @brief Reads the current branch-tree root hash from a BRANCH object.
 *
 * Only valid for handles opened on BRANCH objects. The returned string is
 * the merkle_root of the BRANCH_TREE this branch currently points at — a
 * `Merkle<std::string>` root mapping mg_name → mg_hash. An empty string
 * indicates an unborn branch with no commits yet.
 *
 * @param handle           Handle to a BRANCH object.
 * @param target_hash_out  Set to a heap-allocated copy of the target hash
 *                         (possibly empty). Release with rnt_free_string().
 * @return 0 on success, negative when the handle is not a BRANCH.
 */
NT_API int rnt_branch_target(rnt_handle_t handle, char** target_hash_out);

/**
 * @brief Atomically advances a branch to point at a new branch-tree root.
 *
 * The new hash must already exist as a content-addressed blob in the KV
 * store — branch-tree roots are produced by mutating calls (rnt_link_tuple,
 * rnt_register_relation, …) and cannot be invented externally. Pass an
 * empty string to reset the branch to unborn.
 *
 * Write exclusion is structural: BRANCH objects carry @c exclusive=true in
 * their object_type, so LifecycleManager::Contention prevents a second handle
 * from being opened while a writer holds the branch. AUTH_CLAIM::WRITE is not
 * checked at the C API boundary.
 *
 * @param branch_path  Slash-separated branch path, e.g. "/system/branches/main".
 * @param new_hash     Branch-tree root to advance to (64-char lowercase hex),
 *                     or "" to reset to unborn.
 * @return 0 on success, negative when the branch is not found, the type is
 *         not BRANCH, or @p new_hash is non-empty but no matching blob
 *         exists in the KV store.
 */
NT_API int rnt_branch_advance(const char* branch_path, const char* new_hash);

/* ------------------------------------------------------------------ */
/* Object registration                                                  */
/* ------------------------------------------------------------------ */

/**
 * @brief Registers a RELATION object at the given path.
 *
 * Idempotent: if an object already exists at the path, the call succeeds
 * without re-registering.
 *
 * @param path Slash-separated path of the form
 *             "/system/branches/<branch>/multigroups/<mg>/relations/<rel>".
 * @return 0 on success, negative on error.
 */
NT_API int rnt_register_relation(const char* path);

/**
 * @brief Registers a BRANCH object at the given path pointing at a branch-tree root.
 *
 * If a BRANCH already exists at this path, returns 0 without modifying it.
 * When @p target_hash is non-NULL and non-empty it must already exist as a
 * content-addressed blob in the KV store (produced by an earlier mutation
 * through the same runtime); otherwise the call fails. Pass NULL or "" to
 * register an unborn branch.
 *
 * @param path         Slash-separated path, e.g. "/system/branches/main".
 * @param target_hash  Branch-tree root this branch points at (may be NULL
 *                     or "" for unborn).
 * @return 0 on success, negative on error.
 */
NT_API int rnt_register_branch(const char* path, const char* target_hash);

/**
 * @brief Lists all relations of a specific multigroup under a branch.
 *
 * Returns a newline-delimited string of "name\troot_hash" pairs, one per
 * relation in the named multigroup at the branch's current tree. An unborn
 * branch or an mg absent from the branch tree yields an empty string. The
 * caller must release the string with rnt_free_string().
 *
 * @param branch_mg_path  Slash-separated path of the form
 *                        "/system/branches/<name>/multigroups/<mg>".
 * @param out             Set to a heap-allocated "name\troot\n" string.
 *                        Release with rnt_free_string(). NULL on error.
 * @return 0 on success, negative when the path shape is wrong or the branch
 *         is not found.
 */
NT_API int rnt_list_relations(const char* branch_mg_path, char** out);

/**
 * @brief Lists all multigroups bound to a branch.
 *
 * Reads the branch-tree root from `/system/branches/<name>` and pages each
 * (mg_name, mg_hash) entry. Returns "name\thash\n" lines. Unborn branches
 * yield an empty string.
 *
 * @param branch_path  Slash-separated branch path, e.g. "/system/branches/main".
 * @param out          Heap-allocated string; release with rnt_free_string().
 * @return 0 on success, negative when the branch is not found.
 */
NT_API int rnt_list_branch_multigroups(const char* branch_path, char** out);

/**
 * @brief Lists all relations stored in a specific snapshot.
 *
 * Reads the multigroup codec directly from the snapshot at
 * /system/snapshots/<snapshot_hash> without following any branch HEAD.
 * Use this when the desired snapshot hash is already known (e.g. detached
 * checkout). Returns the same "name\troot_hash\n" format as rnt_list_relations.
 *
 * @param snapshot_hash  64-char hex hash of the snapshot.
 * @param out            Set to heap-allocated string. Release with rnt_free_string().
 * @return 0 on success, negative when the snapshot is not registered.
 */
NT_API int rnt_list_snapshot_relations(const char* snapshot_hash, char** out);

/* ------------------------------------------------------------------ */
/* Tuple storage                                                        */
/* ------------------------------------------------------------------ */

/**
 * @brief Stores a tuple and links it to the relation at the given path.
 *
 * Tuples are encoded as a flat key=value string, one attribute per line:
 *   "name=Blathers\nprofession=Museum Curator\n"
 * Attributes are sorted by name before hashing to ensure content-addressing
 * is order-independent.
 *
 * @param relation_path  Slash-separated relation path.
 * @param kv_attrs       Newline-delimited "key=value" attribute string.
 * @param hash_out       Set to the 64-character hex SHA-256 hash of the tuple.
 *                       Release with rnt_free_string(). NULL on error.
 * @return 0 on success, negative on error.
 */
NT_API int rnt_link_tuple(const char* relation_path,
                          const char* kv_attrs,
                          char**      hash_out);

/**
 * @brief Removes a tuple from the relation's Merkle tree and tuple store.
 *
 * Calls Merkle::Remove on the relation's current root and updates
 * ObjectManager::Relation::merkle_root atomically.  The tuple bytes remain in
 * the KV store (they may be referenced by older snapshots); only the
 * membership in the current tree is removed.
 *
 * @param relation_path  Slash-separated relation path.
 * @param tuple_hash     64-character hex SHA-256 of the tuple to remove.
 * @return 0 on success, negative when the relation is not found.
 */
NT_API int rnt_unlink_tuple(const char* relation_path, const char* tuple_hash);

/**
 * @brief Resets a relation's Merkle root to the empty-tree state.
 *
 * Sets ObjectManager::Relation::merkle_root to the empty string.  Existing
 * tuple bytes remain in the KV store.  After this call the relation contains
 * no tuples from the perspective of cursor iteration.
 *
 * @param relation_path  Slash-separated relation path.
 * @return 0 on success, negative when the relation is not found.
 */
NT_API int rnt_clear_relation(const char* relation_path);

/**
 * @brief Returns the current Merkle root hash for a relation.
 *
 * OCaml calls this after rnt_link_tuple or rnt_unlink_tuple to read the
 * updated root back and store it in the in-memory multigroup (tree_pointer).
 * An empty string is returned for a relation that contains no tuples.
 *
 * @param relation_path  Slash-separated relation path.
 * @param root_hash_out  Set to the 64-character hex root hash, or to an empty
 *                       heap-allocated string for an empty relation.
 *                       Release with rnt_free_string().
 * @return 0 on success, negative when the relation is not found.
 */
NT_API int rnt_relation_root(const char* relation_path, char** root_hash_out);

/* ------------------------------------------------------------------ */
/* Cursor and VM                                                        */
/* ------------------------------------------------------------------ */

/**
 * @brief Opens a cursor on the relation referenced by handle.
 *
 * The returned cursor is positioned before the first tuple. Advance it with
 * rnt_cursor_next(). The cursor must be closed with rnt_cursor_close() before
 * closing the handle.
 *
 * @param handle  Open RELATION handle.
 * @return Cursor pointer, or NULL on error.
 */
NT_API rnt_cursor_t rnt_cursor_open(rnt_handle_t handle);

/**
 * @brief Advances the cursor and returns the next tuple as a kv string.
 *
 * The tuple is encoded as a newline-delimited "key=value" string matching the
 * format accepted by rnt_link_tuple:
 *   "name=Blathers\nprofession=Museum Curator\n"
 *
 * @param cursor       Open cursor.
 * @param tuple_out    Set to a heap-allocated kv string, or NULL when exhausted.
 *                     Release with rnt_free_string() when non-NULL.
 * @return 1 when a tuple was returned, 0 when exhausted, negative on error.
 */
NT_API int rnt_cursor_next(rnt_cursor_t cursor, char** tuple_out);

/**
 * @brief Closes a cursor and releases its resources.
 * @return 0 on success, negative on error.
 */
NT_API int rnt_cursor_close(rnt_cursor_t cursor);

/* ------------------------------------------------------------------ */
/* VM plan builder                                                      */
/* ------------------------------------------------------------------ */

/** Opaque plan node tree, built incrementally via rnt_plan_* calls. */
typedef void* rnt_plan_t;

/**
 * @brief Creates a SCAN plan node that reads all tuples from a stored relation.
 *
 * Opens a RELATION handle and cursor internally. Both are transferred to the
 * plan and released when the resulting VM cursor is closed.
 *
 * @param relation_path  Absolute slash-separated path to the relation, e.g.
 *  "/system/branches/main/multigroups/warehouse/relations/public:users".
 * @return Plan node, or NULL when the relation does not exist or cannot be opened.
 */
NT_API rnt_plan_t rnt_plan_scan(const char* relation_path);

/**
 * @brief Creates a nested-loop JOIN plan node.
 *
 * Takes ownership of both @p left and @p right. On success the caller must not
 * free either child; they are released when the returned plan is freed or
 * executed. On failure (NULL return) both children are freed.
 *
 * @return Plan node, or NULL on error.
 */
NT_API rnt_plan_t rnt_plan_join(rnt_plan_t left, rnt_plan_t right);

/**
 * @brief Creates a TAKE plan node that limits output to at most @p limit tuples.
 *
 * Takes ownership of @p source. On failure @p source is freed.
 *
 * @return Plan node, or NULL on error.
 */
NT_API rnt_plan_t rnt_plan_take(rnt_plan_t source, size_t limit);

/**
 * @brief Releases a plan that was built but not yet executed.
 *
 * Closes any open cursors and handles owned by the plan tree, then frees all
 * nodes. Safe to call with NULL. Do NOT call after rnt_vm_execute_plan —
 * that function takes ownership.
 */
NT_API void rnt_plan_free(rnt_plan_t plan);

/**
 * @brief Executes a plan tree and returns a streaming VM cursor.
 *
 * Takes ownership of @p plan; the caller must not call rnt_plan_free after this.
 * The returned cursor is advanced with rnt_vm_cursor_next() and must be closed
 * with rnt_vm_cursor_close(), which also releases all plan resources.
 *
 * @return VM cursor, or NULL when the plan is NULL.
 */
NT_API rnt_cursor_t rnt_vm_execute_plan(rnt_plan_t plan);

/**
 * @brief Advances a VM cursor and returns the next merged tuple as a kv string.
 *
 * Same encoding and return-value semantics as rnt_cursor_next.
 *
 * @param vm_cursor  Cursor returned by rnt_vm_execute_plan.
 * @param tuple_out  Set to a heap-allocated kv string, or NULL when exhausted.
 *                   Release with rnt_free_string() when non-NULL.
 * @return 1 when a tuple was returned, 0 when exhausted, negative on error.
 */
NT_API int rnt_vm_cursor_next(rnt_cursor_t vm_cursor, char** tuple_out);

/**
 * @brief Closes a VM cursor, releases all plan nodes, cursors, and handles.
 * @return 0 on success, negative on error.
 */
NT_API int rnt_vm_cursor_close(rnt_cursor_t vm_cursor);

/* ------------------------------------------------------------------ */
/* Memory management                                                    */
/* ------------------------------------------------------------------ */

/** @brief Releases a string allocated by the API. Safe to call with NULL. */
NT_API void rnt_free_string(char* s);

/** @brief Releases a byte buffer allocated by the API. Safe to call with NULL. */
NT_API void rnt_free_bytes(uint8_t* p);

#ifdef __cplusplus
}
#endif