@x12i/memorix-hippox
v1.35.0
Published
Memorix Hippox: discover, simulate, persist, and resolve contextual relationships between Memorix object types.
Maintainers
Readme
@x12i/memorix-hippox
Memorix Hippox stores reusable contextual links between Memorix object types. Operators can discover join keys from sampled data, define property-based joins without pre-modeled schema relations, simulate match quality, approve links into long-term memory, and resolve bounded context into jobMemory.context.
See memorix-format.md §13 and memorix-fomat-additions.md §K for how Hippox fits the Memorix 2.0 record model (data, associatedData, index.recordLinks, sourceFacts).
Database
Approved and draft links persist in Mongo database memorix-hippox (collection context-links by default).
| Variable | Default | Purpose |
|----------|---------|---------|
| MEMORIX_HIPPOX_DB | memorix-hippox | Hippox Mongo database |
| MEMORIX_HIPPOX_COLLECTION | context-links | Context links collection |
| MEMORIX_HIPPOX_SIMULATION_LIMIT | 1000 | Max records sampled per side during simulation |
| MEMORIX_HIPPOX_SIMULATION_EXAMPLES_LIMIT | 25 | Max example matches/misses in simulation summary |
| MEMORIX_CONTEXT_LIST_LIMIT | 25 | Default list retrieval limit at resolve time |
| MEMORIX_HIPPOX_DISCOVERY_SAMPLE_LIMIT | 1000 | Max records sampled per side during join discovery |
| MEMORIX_HIPPOX_DISCOVERY_DISTINCT_CAP | 500 | Max distinct source values per value probe |
| MEMORIX_HIPPOX_DISCOVERY_MAX_PAIRS | 200 | Max source×target field pairs to evaluate |
| MEMORIX_HIPPOX_DISCOVERY_MIN_COVERAGE | 0.05 | Minimum field coverage to include in discovery |
| MEMORIX_HIPPOX_DISCOVERY_MIN_PAIR_SCORE | 0.15 | Minimum score for join candidates / recommended join |
| MEMORIX_HIPPOX_DISCOVERY_CACHE_TTL_MS | 300000 | In-memory join-discovery cache TTL per pair |
| MEMORIX_HIPPOX_DISCOVERY_MAX_CANDIDATES | 50 | Max entries in joinCandidates |
Path conventions (Memorix 2.0)
Join paths are dot paths from the document root, consistent with entity descriptor relations:
{ "sourcePath": "data.subnetId", "targetPath": "data.id", "operator": "eq" }Discovery walks the content-type dataRoot (default data) and returns paths with the data. prefix.
Record identity and sorting support both Memorix 2.0 and legacy shapes:
- IDs:
identity.recordId,identity.targetId, then legacyentityId/recordId/_id - Latest record:
data.scopedAt, thenlifecycle.capturedAt/lifecycle.modifiedAt, then root timestamps
Example: assets → subnets (record context)
{
"sourceObjectType": "assets",
"targetObjectType": "subnets",
"direction": "downstream",
"join": [
{ "sourcePath": "data.subnetId", "targetPath": "data.id", "operator": "eq" }
],
"retrieval": { "type": "record", "outputMode": "data" }
}Resolved output:
{
"jobMemory": {
"context": {
"subnets": {
"cidr": "10.0.0.0/24",
"region": "eu-west-1"
}
}
}
}Materializing this into a snapshot's associatedData block is a separate assembly step; Hippox resolve returns transient job context.
HTTP routes
When mounted through memorix-catalox-api:
| Method | Path | Purpose |
|--------|------|---------|
| GET | /api/hippox/links?sourceObjectType=assets | List draft + simulated + approved links |
| POST | /api/hippox/links | Create draft link |
| GET | /api/hippox/join-discovery?sourceObjectType=…&targetObjectType=… | Rank join fields and recommend default pair (findMode=field-join default) |
| GET | /api/hippox/join-discovery?…&findMode=identifier | Mode A: source value paths → target identity / concept candidates |
| POST | /api/hippox/join-discovery | Same as GET with body parameters (findMode optional) |
| POST | /api/hippox/infer-links | Fuzzy-map joins across object types (@x12i/helpers ≥ 2.0) |
| POST | /api/hippox/links/from-relation-candidate | Create Hippox draft from schema relation candidate |
| POST | /api/hippox/links/simulate | Simulate unsaved definition |
| POST | /api/hippox/links/:id/simulate | Simulate + persist summary |
| POST | /api/hippox/links/:id/approve | Approve simulated link |
| POST | /api/hippox/context/resolve | Resolve runtime context from approved links |
Join discovery response (summary)
Returns ranked sourceFields, targetFields, joinCandidates, and recommendedJoin for Mode B (findMode=field-join, default). Each field includes a classification of link-target | join-out | other; pure link-target identity fields are demoted so Mode B dropdowns do not treat them as outward join keys.
With findMode=identifier (Mode A), the response also includes sourceValuePaths, targetIdentityCandidates, and recommendedMatch (or null + warnings). Mode A does not require a target data.* join path — the target side is identity / concept matching.
Discovery is ephemeral (optional in-memory cache); only approved links persist.
Programmatic usage
import { createMemorixRetrievalStackFromEnv } from '@x12i/memorix-retrieval';
import { HippoxService } from '@x12i/memorix-hippox';
const { client } = await createMemorixRetrievalStackFromEnv();
const hippox = await HippoxService.create({ retrieval: client });
const discovery = await hippox.discoverJoinCandidates({
sourceObjectType: 'assets',
targetObjectType: 'subnets',
});
const draft = await hippox.createDraftLink({
sourceObjectType: 'assets',
targetObjectType: 'subnets',
direction: 'downstream',
join: [discovery.recommendedJoin ?? {
sourcePath: 'data.subnetId',
targetPath: 'data.id',
}],
retrieval: { type: 'record', outputMode: 'data' },
});
const { simulation } = await hippox.simulateAndPersist(draft.id);
await hippox.approveLink(draft.id);
const resolved = await hippox.resolveContextInput({
sourceObjectType: 'assets',
sourceRecordId: 'entity:asset:10.100.18.64',
linkIds: [draft.id],
});Legacy relation links
Relation-based JobContextLink / contextLinks are not resolved into
jobMemory. Prefer Hippox approved links (resolveContext /
resolveContextInput). resolveMixedContext() remains as a deprecated alias
that only forwards Hippox resolution (no _legacy merge).
Retrieval types
- record — single best match (default latest
data.scopedAtorlifecycle.capturedAt) - list — bounded array (mandatory limit)
- aggregate — flat per-property value counts
Output modes: data (default), record, dataWithRecordId.
Relationship to other Memorix layers
| Layer | Hippox role |
|-------|-------------|
| sourceFacts.resolver.component: "memorix-hippox" | value resolution rules (enum/lookup) — separate from context links |
| index.recordLinks | schema-declared indexed relations |
| associatedData | readable materialized join output on snapshots |
| jobMemory.context | runtime output of context/resolve |
Fuzzy mapping pipeline (@x12i/helpers ≥ 2.0)
@x12i/[email protected] ships record key analysis at @x12i/helpers/record-key-analysis. Hippox orchestrates it via POST /api/hippox/infer-links.
This complements join-discovery (named object types, schema seeds, value overlap ranking). Fuzzy mapping is the cold-start path; join-discovery refines once types and collections are registered.
Prerequisites
- Runtime access to collection samples (Mongo via Hippox collection resolver).
- Optional peer
@x12i/helpers@^2.0.0(returns HTTP 501 if missing when calling infer-links).
API
POST /api/hippox/infer-links
{ "objectTypes": ["assets", "subnets"], "sampleLimit": 10000 }Returns collectionProfiles (PRIMARY / FOREIGN / ENUM tags) and ranked inferredLinks with inclusion ratios.
Programmatic usage
const inference = await hippox.inferLinks({
objectTypes: ['assets', 'subnets', 'vulnerabilities'],
});
for (const link of inference.inferredLinks) {
await hippox.createDraftLink({
sourceObjectType: link.sourceObjectType,
targetObjectType: link.targetObjectType,
direction: 'downstream',
join: [{ sourcePath: link.sourcePath, targetPath: link.targetPath }],
retrieval: { type: 'record', outputMode: 'data' },
});
}Schema relation bridge
POST /api/hippox/links/from-relation-candidate
POST /api/relationships/:sourceEntity/:relationKey/hippox-draft # schema-api operational wrapperConverts a schema RelationCandidate or declared relation into a Hippox draft link.
See memorix-format.md §13.4 and memorix-fomat-additions.md §K.4.
