Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,14 @@ exposes the stable permission-aware
search contract, immutable evidence, secure result, and verified grounding as
the exact `knowledge::search` named interface. Assistant and Asset Registry
consume that parent interface without importing Retrieval implementation types.
Retrieval remains explicitly open while its Graph verifier and remaining
sibling adapters are replaced by intentional APIs. The
Graph exploration, export, and curation obtain their immutable authorized
evidence snapshot and exact current governing-evidence decision through the
Retrieval-owned `GraphEvidenceVerifier`; Graph does not import Retrieval scope
resolution, candidate, or store implementation types. Verified snapshots reject
unknown Knowledge Spaces, and canonical evidence rechecks carry only the assets
authorized for the requested Space. Retrieval remains
explicitly open while its remaining sibling adapters are replaced by
intentional APIs. The
provider-neutral object-storage port is exposed as the
`knowledge::storage` named interface. Leased database jobs carry ingestion work
across processes. A specific Knowledge Asset
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.orgmemory.core.knowledge.graph;

import com.orgmemory.core.knowledge.retrieval.GraphEvidenceVerifier;
import com.orgmemory.core.knowledge.retrieval.KnowledgeRetrievalUnavailableException;
import com.orgmemory.core.knowledge.retrieval.VerifiedGraphEvidenceScope;
import com.orgmemory.core.organization.CurrentActor;

/** Shared Graph-side translation for unavailable canonical evidence scopes. */
final class GraphEvidenceScopeAccess {

private GraphEvidenceScopeAccess() {}

static VerifiedGraphEvidenceScope verify(
GraphEvidenceVerifier verifier,
CurrentActor actor,
String authorizationModelId,
String unavailableMessage) {
try {
return verifier.verifyScope(actor, authorizationModelId);
} catch (KnowledgeRetrievalUnavailableException unavailable) {
throw new KnowledgeRetrievalUnavailableException(
unavailableMessage, unavailable);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
package com.orgmemory.core.knowledge.graph;

import com.orgmemory.core.knowledge.retrieval.KnowledgeEvidenceScopeResolver;
import com.orgmemory.core.knowledge.retrieval.KnowledgeEvidenceScopeUnavailableException;
import com.orgmemory.core.knowledge.retrieval.GraphEvidenceVerifier;
import com.orgmemory.core.knowledge.asset.KnowledgeProjectionNamespaces;
import com.orgmemory.core.shared.error.KnowledgeResourceNotFoundException;
import com.orgmemory.core.knowledge.retrieval.KnowledgeRetrievalUnavailableException;
import com.orgmemory.core.knowledge.retrieval.ResolvedKnowledgeEvidenceScope;
import com.orgmemory.core.knowledge.retrieval.SecureKnowledgeRetrievalStore;
import com.orgmemory.core.knowledge.retrieval.VerifiedGraphEvidenceScope;
import com.orgmemory.core.knowledge.asset.KnowledgeAssetGraphQuery;

import com.orgmemory.core.authorization.AuthorizationDecision;
Expand Down Expand Up @@ -45,8 +43,7 @@ public class KnowledgeGraphCurationService {
private final KnowledgeSpaceQuery spaces;
private final KnowledgeAssetGraphQuery assets;
private final RelationshipAuthorizationPort authorization;
private final KnowledgeEvidenceScopeResolver evidenceScopes;
private final SecureKnowledgeRetrievalStore canonicalEvidence;
private final GraphEvidenceVerifier evidenceVerifier;
private final GraphExportReader graphs;
private final GraphCurationStore curations;
private final ModelInvocationCache modelCache;
Expand All @@ -56,17 +53,15 @@ public class KnowledgeGraphCurationService {
KnowledgeSpaceQuery spaces,
KnowledgeAssetGraphQuery assets,
RelationshipAuthorizationPort authorization,
KnowledgeEvidenceScopeResolver evidenceScopes,
SecureKnowledgeRetrievalStore canonicalEvidence,
GraphEvidenceVerifier evidenceVerifier,
GraphExportReader graphs,
GraphCurationStore curations,
ModelInvocationCache modelCache,
RetrievalResultCache retrievalCache) {
this.spaces = spaces;
this.assets = assets;
this.authorization = authorization;
this.evidenceScopes = evidenceScopes;
this.canonicalEvidence = canonicalEvidence;
this.evidenceVerifier = evidenceVerifier;
this.graphs = graphs;
this.curations = curations;
this.modelCache = modelCache;
Expand All @@ -83,7 +78,7 @@ public GraphCurationRecord apply(
actor, command.knowledgeSpaceId());
ProjectionNamespace namespace =
namespace(actor.organizationId(), command.knowledgeSpaceId());
ResolvedKnowledgeEvidenceScope resolved =
VerifiedGraphEvidenceScope resolved =
resolve(actor, decision.policyVersion());
requireCurrentScope(command, resolved);
CurationProvenance provenance = new CurationProvenance(
Expand Down Expand Up @@ -198,11 +193,11 @@ public void deactivate(
requireSpace(actor, knowledgeSpaceId);
AuthorizationDecision decision =
requirePermission(actor, knowledgeSpaceId);
ResolvedKnowledgeEvidenceScope resolved =
VerifiedGraphEvidenceScope resolved =
resolve(actor, decision.policyVersion());
if (resolved.aclGenerationByKnowledgeSpace()
.getOrDefault(knowledgeSpaceId, 0L)
!= authorizationGeneration) {
if (!resolved.includesKnowledgeSpace(knowledgeSpaceId)
|| resolved.authorizationGeneration(knowledgeSpaceId)
!= authorizationGeneration) {
throw new KnowledgeRetrievalUnavailableException(
"Knowledge graph authorization changed before curation");
}
Expand All @@ -229,50 +224,39 @@ private void requireGoverningEvidence(
CurrentActor actor,
UUID knowledgeSpaceId,
com.orgmemory.graphrag.model.EvidenceReference evidence,
ResolvedKnowledgeEvidenceScope resolved) {
VerifiedGraphEvidenceScope resolved) {
if (!actor.organizationId().equals(evidence.organizationId())) {
throw new KnowledgeResourceNotFoundException();
}
assets.requireInSpace(
actor.organizationId(), evidence.knowledgeAssetId(), knowledgeSpaceId);
var spaceScope = resolved.forKnowledgeSpace(knowledgeSpaceId);
if (!spaceScope.includes(
if (!resolved.includes(
knowledgeSpaceId,
evidence.organizationId(), evidence.knowledgeAssetId())) {
throw new OrgMemoryAccessDeniedException(
"Governing evidence is not visible to the current actor");
}
var candidates = canonicalEvidence.recheck(
retrievalScope(resolved),
java.util.List.of(Objects.requireNonNull(
evidence.chunkId(), "governing evidence chunkId")));
boolean current = candidates.size() == 1
&& candidates.getFirst().knowledgeAssetId()
.equals(evidence.knowledgeAssetId())
&& candidates.getFirst().sourceRevisionId()
.equals(evidence.sourceRevisionId())
&& candidates.getFirst().currentAclSnapshotId()
.equals(evidence.aclSnapshotId());
if (!current) {
if (!evidenceVerifier.isCurrentGoverningEvidence(
resolved, knowledgeSpaceId, evidence)) {
throw new OrgMemoryAccessDeniedException(
"Governing evidence is stale or unavailable");
}
}

private void requireCurrentScope(
KnowledgeGraphCurationCommand command,
ResolvedKnowledgeEvidenceScope resolved) {
VerifiedGraphEvidenceScope resolved) {
UUID spaceId = command.knowledgeSpaceId();
if (!resolved.knowledgeSpaceIds().contains(spaceId)
|| resolved.aclGenerationByKnowledgeSpace()
.getOrDefault(spaceId, 0L)
if (!resolved.includesKnowledgeSpace(spaceId)
|| resolved.authorizationGeneration(spaceId)
!= command.authorizationGeneration()) {
throw new KnowledgeRetrievalUnavailableException(
"Knowledge graph authorization changed before curation");
}
}

private void requireVisibleEntity(
ResolvedKnowledgeEvidenceScope resolved,
VerifiedGraphEvidenceScope resolved,
ProjectionNamespace namespace,
UUID knowledgeSpaceId,
UUID entityId) {
Expand All @@ -285,7 +269,7 @@ private void requireVisibleEntity(
}

private void requireVisibleIdentity(
ResolvedKnowledgeEvidenceScope resolved,
VerifiedGraphEvidenceScope resolved,
ProjectionNamespace namespace,
UUID knowledgeSpaceId,
com.orgmemory.graphrag.curation.GraphIdentityKind kind,
Expand All @@ -305,42 +289,29 @@ private void requireVisibleIdentity(
}
}

private ResolvedKnowledgeEvidenceScope resolve(
private VerifiedGraphEvidenceScope resolve(
CurrentActor actor,
String authorizationModelId) {
try {
return evidenceScopes.resolve(actor, authorizationModelId);
} catch (KnowledgeEvidenceScopeUnavailableException unavailable) {
throw new KnowledgeRetrievalUnavailableException(
"Knowledge graph permissions are temporarily unavailable");
}
return GraphEvidenceScopeAccess.verify(
evidenceVerifier,
actor,
authorizationModelId,
"Knowledge graph permissions are temporarily unavailable");
}
Comment thread
kl3inIT marked this conversation as resolved.

private void requireUnchangedScope(
CurrentActor actor,
UUID knowledgeSpaceId,
String authorizationModelId,
ResolvedKnowledgeEvidenceScope initial) {
ResolvedKnowledgeEvidenceScope current =
VerifiedGraphEvidenceScope initial) {
VerifiedGraphEvidenceScope current =
resolve(actor, authorizationModelId);
if (!initial.forKnowledgeSpace(knowledgeSpaceId)
.authorizedAssetIds()
.equals(current.forKnowledgeSpace(knowledgeSpaceId)
.authorizedAssetIds())
|| initial.aclGenerationByKnowledgeSpace()
.getOrDefault(knowledgeSpaceId, 0L)
!= current.aclGenerationByKnowledgeSpace()
.getOrDefault(knowledgeSpaceId, 0L)) {
if (!initial.hasSameAssetsAndGeneration(current, knowledgeSpaceId)) {
throw new KnowledgeRetrievalUnavailableException(
"Knowledge graph authorization changed during curation");
}
}

private static SecureKnowledgeRetrievalStore.RetrievalScope retrievalScope(
ResolvedKnowledgeEvidenceScope scope) {
return scope.toRetrievalScope();
}

private void requireSpace(CurrentActor actor, UUID knowledgeSpaceId) {
if (!spaces.isActive(actor.organizationId(), knowledgeSpaceId)) {
throw new OrgMemoryAccessDeniedException(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.orgmemory.core.knowledge.graph;

import com.orgmemory.core.knowledge.retrieval.KnowledgeEvidenceScopeResolver;
import com.orgmemory.core.knowledge.retrieval.GraphEvidenceVerifier;
import com.orgmemory.core.authorization.RelationshipAuthorizationPort;
import com.orgmemory.core.knowledge.space.KnowledgeSpaceQuery;
import com.orgmemory.core.permission.PermissionAuditService;
Expand All @@ -17,14 +17,14 @@ public class KnowledgeGraphExplorerConfiguration {
KnowledgeGraphExplorerService knowledgeGraphExplorerService(
KnowledgeSpaceQuery spaces,
RelationshipAuthorizationPort authorization,
KnowledgeEvidenceScopeResolver evidenceScopes,
GraphEvidenceVerifier evidenceVerifier,
GraphExportReader graphs,
GraphExplorerProperties properties,
PermissionAuditService audit) {
return new KnowledgeGraphExplorerService(
spaces,
authorization,
evidenceScopes,
evidenceVerifier,
graphs,
properties,
audit);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
package com.orgmemory.core.knowledge.graph;

import com.orgmemory.core.knowledge.retrieval.KnowledgeEvidenceScopeResolver;
import com.orgmemory.core.knowledge.retrieval.KnowledgeEvidenceScopeUnavailableException;
import com.orgmemory.core.knowledge.asset.KnowledgeProjectionNamespaces;
import com.orgmemory.core.knowledge.retrieval.GraphEvidenceVerifier;
import com.orgmemory.core.knowledge.retrieval.KnowledgeRetrievalUnavailableException;
import com.orgmemory.core.knowledge.retrieval.ResolvedKnowledgeEvidenceScope;
import com.orgmemory.core.knowledge.retrieval.VerifiedGraphEvidenceScope;
import com.orgmemory.core.authorization.AuthorizationDecision;
import com.orgmemory.core.authorization.PermissionKey;
import com.orgmemory.core.authorization.RelationshipAuthorizationPort;
Expand Down Expand Up @@ -41,21 +40,21 @@ public class KnowledgeGraphExplorerService {

private final KnowledgeSpaceQuery spaces;
private final RelationshipAuthorizationPort authorization;
private final KnowledgeEvidenceScopeResolver evidenceScopes;
private final GraphEvidenceVerifier evidenceVerifier;
private final GraphExportReader graphs;
private final GraphExplorerProperties properties;
private final PermissionAuditService audit;

public KnowledgeGraphExplorerService(
KnowledgeSpaceQuery spaces,
RelationshipAuthorizationPort authorization,
KnowledgeEvidenceScopeResolver evidenceScopes,
GraphEvidenceVerifier evidenceVerifier,
GraphExportReader graphs,
GraphExplorerProperties properties,
PermissionAuditService audit) {
this.spaces = spaces;
this.authorization = authorization;
this.evidenceScopes = evidenceScopes;
this.evidenceVerifier = evidenceVerifier;
this.graphs = graphs;
this.properties = properties;
this.audit = audit;
Expand Down Expand Up @@ -98,9 +97,9 @@ private KnowledgeGraphView explore(
String requestId,
String policyVersion,
int attempt) {
ResolvedKnowledgeEvidenceScope initial =
VerifiedGraphEvidenceScope initial =
resolve(actor, policyVersion);
if (!initial.knowledgeSpaceIds().contains(knowledgeSpaceId)) {
if (!initial.includesKnowledgeSpace(knowledgeSpaceId)) {
return empty(
actor,
knowledgeSpaceId,
Expand All @@ -114,9 +113,9 @@ private KnowledgeGraphView explore(
initial.forKnowledgeSpace(knowledgeSpaceId),
namespace);

ResolvedKnowledgeEvidenceScope current =
VerifiedGraphEvidenceScope current =
resolve(actor, policyVersion);
if (!sameSpaceScope(initial, current, knowledgeSpaceId)) {
if (!initial.hasSameSpaceScope(current, knowledgeSpaceId)) {
if (attempt == 0) {
return explore(
actor,
Expand Down Expand Up @@ -154,8 +153,7 @@ private KnowledgeGraphView explore(
entityLimit,
properties.maximumRelationLimit(),
maximumDepth,
initial.aclGenerationByKnowledgeSpace()
.getOrDefault(knowledgeSpaceId, 0L),
initial.authorizationGeneration(knowledgeSpaceId),
curationDecision.allowed());
audit.record(new PermissionAuditCommand(
actor.organizationId(),
Expand Down Expand Up @@ -191,15 +189,14 @@ private String requireSpaceAccess(
return decision.policyVersion();
}

private ResolvedKnowledgeEvidenceScope resolve(
private VerifiedGraphEvidenceScope resolve(
CurrentActor actor,
String policyVersion) {
try {
return evidenceScopes.resolve(actor, policyVersion);
} catch (KnowledgeEvidenceScopeUnavailableException unavailable) {
throw new KnowledgeRetrievalUnavailableException(
"Knowledge graph permissions are temporarily unavailable");
}
return GraphEvidenceScopeAccess.verify(
evidenceVerifier,
actor,
policyVersion,
"Knowledge graph permissions are temporarily unavailable");
}

private KnowledgeGraphView empty(
Expand Down Expand Up @@ -406,18 +403,6 @@ private static boolean contains(String value, String needle) {
return value.toLowerCase(Locale.ROOT).contains(needle);
}

private static boolean sameSpaceScope(
ResolvedKnowledgeEvidenceScope initial,
ResolvedKnowledgeEvidenceScope current,
UUID knowledgeSpaceId) {
return initial.authorizationModelId()
.equals(current.authorizationModelId())
&& initial.forKnowledgeSpace(knowledgeSpaceId)
.authorizationFingerprint()
.equals(current.forKnowledgeSpace(knowledgeSpaceId)
.authorizationFingerprint());
}

private String normalizeQuery(String query) {
if (query == null || query.isBlank()) {
return "";
Expand Down
Loading