contextsynapse 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- contextsynapse/__init__.py +107 -0
- contextsynapse/__main__.py +9 -0
- contextsynapse/a2a/__init__.py +28 -0
- contextsynapse/a2a/client.py +120 -0
- contextsynapse/a2a/discovery.py +128 -0
- contextsynapse/a2a/handlers.py +164 -0
- contextsynapse/a2a/models.py +246 -0
- contextsynapse/a2a/streaming.py +92 -0
- contextsynapse/a2a/task_manager.py +275 -0
- contextsynapse/adapters/__init__.py +98 -0
- contextsynapse/adapters/_base.py +298 -0
- contextsynapse/adapters/autogen/__init__.py +4 -0
- contextsynapse/adapters/autogen/tools.py +34 -0
- contextsynapse/adapters/crewai/__init__.py +4 -0
- contextsynapse/adapters/crewai/tools.py +63 -0
- contextsynapse/adapters/langchain/__init__.py +19 -0
- contextsynapse/adapters/langchain/message_history.py +17 -0
- contextsynapse/adapters/langchain/retriever.py +158 -0
- contextsynapse/adapters/langchain/tools.py +43 -0
- contextsynapse/adapters/langgraph/__init__.py +21 -0
- contextsynapse/adapters/langgraph/checkpoint.py +223 -0
- contextsynapse/adapters/langgraph/context_tools.py +341 -0
- contextsynapse/adapters/langgraph/message_history.py +138 -0
- contextsynapse/adapters/langgraph/tools.py +43 -0
- contextsynapse/adapters/llamaindex/__init__.py +4 -0
- contextsynapse/adapters/llamaindex/tools.py +64 -0
- contextsynapse/adapters/openai/__init__.py +24 -0
- contextsynapse/adapters/openai/tools.py +206 -0
- contextsynapse/adapters/pydantic_ai/__init__.py +4 -0
- contextsynapse/adapters/pydantic_ai/tools.py +36 -0
- contextsynapse/adapters/swarm/__init__.py +4 -0
- contextsynapse/adapters/swarm/tools.py +44 -0
- contextsynapse/agents/__init__.py +6 -0
- contextsynapse/agents/auto_capture.py +80 -0
- contextsynapse/agents/dispatcher.py +299 -0
- contextsynapse/agents/notifications.py +112 -0
- contextsynapse/aiql/__init__.py +71 -0
- contextsynapse/aiql/cache.py +341 -0
- contextsynapse/aiql/compiler/__init__.py +62 -0
- contextsynapse/aiql/compiler/optimizer.py +126 -0
- contextsynapse/aiql/compiler/planner.py +366 -0
- contextsynapse/aiql/compiler/statistics.py +235 -0
- contextsynapse/aiql/compiler/validator.py +425 -0
- contextsynapse/aiql/engine/__init__.py +11 -0
- contextsynapse/aiql/engine/chunk_storage.py +2584 -0
- contextsynapse/aiql/engine/executor.py +7737 -0
- contextsynapse/aiql/engine/extraction_integration.py +442 -0
- contextsynapse/aiql/engine/graph_builder.py +405 -0
- contextsynapse/aiql/engine/node_creation_from_normalized.py +691 -0
- contextsynapse/aiql/engine/performance_monitor.py +289 -0
- contextsynapse/aiql/engine/postprocessing.py +365 -0
- contextsynapse/aiql/engine/ray_stage_executors.py +918 -0
- contextsynapse/aiql/engine/redis_cache.py +106 -0
- contextsynapse/aiql/engine/streaming_json.py +366 -0
- contextsynapse/aiql/engine/streaming_parquet.py +568 -0
- contextsynapse/aiql/engine/streaming_ray_chunking.py +419 -0
- contextsynapse/aiql/grammar/__init__.py +14 -0
- contextsynapse/aiql/grammar/aiql_grammar.py +1822 -0
- contextsynapse/aiql/grammar/base.py +71 -0
- contextsynapse/aiql/parser/__init__.py +46 -0
- contextsynapse/aiql/parser/aiql_parser.py +11330 -0
- contextsynapse/aiql/parser/base_parser.py +47 -0
- contextsynapse/api/__init__.py +5 -0
- contextsynapse/api/a2a_router.py +233 -0
- contextsynapse/api/admin_router.py +140 -0
- contextsynapse/api/agent_worker_router.py +978 -0
- contextsynapse/api/algorithms_router.py +317 -0
- contextsynapse/api/api.py +4144 -0
- contextsynapse/api/audit.py +204 -0
- contextsynapse/api/auth.py +441 -0
- contextsynapse/api/auth_router.py +537 -0
- contextsynapse/api/billing.py +261 -0
- contextsynapse/api/billing_router.py +275 -0
- contextsynapse/api/boundary_router.py +452 -0
- contextsynapse/api/cognition_router.py +116 -0
- contextsynapse/api/connector_router.py +237 -0
- contextsynapse/api/context_router.py +1619 -0
- contextsynapse/api/dashboard_router.py +11367 -0
- contextsynapse/api/errors.py +183 -0
- contextsynapse/api/events.py +88 -0
- contextsynapse/api/experiment_bootstrap.py +321 -0
- contextsynapse/api/experiment_orchestrator.py +1078 -0
- contextsynapse/api/experiment_router.py +2089 -0
- contextsynapse/api/feature_gate.py +89 -0
- contextsynapse/api/federation_router.py +108 -0
- contextsynapse/api/integrations.py +567 -0
- contextsynapse/api/integrations_router.py +461 -0
- contextsynapse/api/intelligence_router.py +1028 -0
- contextsynapse/api/metering.py +155 -0
- contextsynapse/api/metrics.py +112 -0
- contextsynapse/api/models.py +38 -0
- contextsynapse/api/monitoring_router.py +174 -0
- contextsynapse/api/pipeline_router.py +1437 -0
- contextsynapse/api/playground_conn.py +116 -0
- contextsynapse/api/projects_router.py +131 -0
- contextsynapse/api/queries_router.py +282 -0
- contextsynapse/api/quota.py +95 -0
- contextsynapse/api/rate_limit.py +64 -0
- contextsynapse/api/search_router.py +261 -0
- contextsynapse/api/shield_router.py +447 -0
- contextsynapse/api/tenant_guard.py +103 -0
- contextsynapse/api/tenant_quotas.py +231 -0
- contextsynapse/api/tenants.py +305 -0
- contextsynapse/api/users.py +646 -0
- contextsynapse/api/v1.py +167 -0
- contextsynapse/api/vertical_builder_router.py +387 -0
- contextsynapse/benchmark/__init__.py +15 -0
- contextsynapse/benchmark/__main__.py +64 -0
- contextsynapse/benchmark/agents.py +338 -0
- contextsynapse/benchmark/live_ab.py +338 -0
- contextsynapse/benchmark/reporter.py +149 -0
- contextsynapse/benchmark/runner.py +264 -0
- contextsynapse/benchmark/savings.py +266 -0
- contextsynapse/benchmark/token_counter.py +212 -0
- contextsynapse/benchmark/workloads.py +289 -0
- contextsynapse/buffer/__init__.py +154 -0
- contextsynapse/buffer/base.py +257 -0
- contextsynapse/buffer/duckdb_buffer.py +246 -0
- contextsynapse/buffer/file_buffer.py +229 -0
- contextsynapse/buffer/lmdb_buffer.py +286 -0
- contextsynapse/buffer/local_buffer.py +154 -0
- contextsynapse/buffer/redis_buffer.py +211 -0
- contextsynapse/buffer/sqlite_buffer.py +266 -0
- contextsynapse/cache.py +2 -0
- contextsynapse/cli/__init__.py +40 -0
- contextsynapse/cli/cli.py +475 -0
- contextsynapse/cli/config_cli.py +177 -0
- contextsynapse/cli/scaffold.py +465 -0
- contextsynapse/cognition/__init__.py +31 -0
- contextsynapse/cognition/derivation.py +55 -0
- contextsynapse/cognition/events.py +135 -0
- contextsynapse/cognition/feedback.py +52 -0
- contextsynapse/cognition/hallucination.py +90 -0
- contextsynapse/cognition/invalidation.py +70 -0
- contextsynapse/cognition/read_tracker.py +50 -0
- contextsynapse/collaboration.py +2 -0
- contextsynapse/comparison.py +2 -0
- contextsynapse/config/__init__.py +10 -0
- contextsynapse/config/connectors/amfi_nav.yaml +18 -0
- contextsynapse/config/connectors/auto_sector.yaml +20 -0
- contextsynapse/config/connectors/banking_sector.yaml +20 -0
- contextsynapse/config/connectors/energy_sector.yaml +20 -0
- contextsynapse/config/connectors/fmcg_sector.yaml +20 -0
- contextsynapse/config/connectors/nsdl_fpi.yaml +9 -0
- contextsynapse/config/connectors/nse_price.yaml +61 -0
- contextsynapse/config/connectors/pharma_sector.yaml +20 -0
- contextsynapse/config/connectors/rbi_dbie.yaml +12 -0
- contextsynapse/config/context_presets/ai_research.json +25 -0
- contextsynapse/config/context_presets/chatgpt_conversations.json +19 -0
- contextsynapse/config/context_presets/claude_conversations.json +19 -0
- contextsynapse/config/context_presets/legal_compliance.json +21 -0
- contextsynapse/config/context_presets/meeting_notes.json +21 -0
- contextsynapse/config/context_presets/slack_engineering.json +21 -0
- contextsynapse/config/context_presets/tech_climate.json +24 -0
- contextsynapse/config/context_presets/toi_politics.json +25 -0
- contextsynapse/config/database_config.py +453 -0
- contextsynapse/config/deployment.py +221 -0
- contextsynapse/config/gpt_action_schema.json +164 -0
- contextsynapse/config/indicators/india_macro.yaml +213 -0
- contextsynapse/config/logging_config.py +88 -0
- contextsynapse/config/mode_config.py +223 -0
- contextsynapse/config/schemas/api_spec.yaml +48 -0
- contextsynapse/config/schemas/architecture_doc.yaml +51 -0
- contextsynapse/config/schemas/chat_history.yaml +90 -0
- contextsynapse/config/schemas/chatgpt_conversation.yaml +101 -0
- contextsynapse/config/schemas/claude_conversation.yaml +148 -0
- contextsynapse/config/schemas/contract.yaml +48 -0
- contextsynapse/config/schemas/conversation.yaml +167 -0
- contextsynapse/config/schemas/database.yaml +74 -0
- contextsynapse/config/schemas/decision.yaml +72 -0
- contextsynapse/config/schemas/ecommerce.yaml +56 -0
- contextsynapse/config/schemas/education.yaml +42 -0
- contextsynapse/config/schemas/healthcare.yaml +133 -0
- contextsynapse/config/schemas/hr_document.yaml +48 -0
- contextsynapse/config/schemas/invoice.yaml +40 -0
- contextsynapse/config/schemas/knowledge_base.yaml +75 -0
- contextsynapse/config/schemas/legal.yaml +42 -0
- contextsynapse/config/schemas/marketing_content.yaml +48 -0
- contextsynapse/config/schemas/meeting_notes.yaml +51 -0
- contextsynapse/config/schemas/news_article.yaml +61 -0
- contextsynapse/config/schemas/real_estate.yaml +49 -0
- contextsynapse/config/schemas/requirements_doc.yaml +48 -0
- contextsynapse/config/schemas/research_paper.yaml +56 -0
- contextsynapse/config/schemas/resume.yaml +49 -0
- contextsynapse/config/schemas/retail.yaml +42 -0
- contextsynapse/config/schemas/rule_meta.yaml +173 -0
- contextsynapse/config/schemas/rules.yaml +72 -0
- contextsynapse/config/schemas/screener_meta.yaml +236 -0
- contextsynapse/config/schemas/sdlc.yaml +103 -0
- contextsynapse/config/schemas/session_graph.yaml +172 -0
- contextsynapse/config/schemas/support_ticket.yaml +48 -0
- contextsynapse/config/schemas/web.yaml +64 -0
- contextsynapse/config/sensors/earnings_calendar.yaml +52 -0
- contextsynapse/config/sensors/global_risk.yaml +53 -0
- contextsynapse/config/sensors/government_policy.yaml +54 -0
- contextsynapse/config/sensors/supply_chain.yaml +50 -0
- contextsynapse/config/sensors/templates/behavioral.yaml +56 -0
- contextsynapse/config/sensors/templates/equity_sentiment.yaml +48 -0
- contextsynapse/config/sensors/templates/global_macro.yaml +47 -0
- contextsynapse/config/sensors/templates/market_data.yaml +118 -0
- contextsynapse/config/sensors/templates/sector_technical.yaml +47 -0
- contextsynapse/config/sensors/weather.yaml +52 -0
- contextsynapse/config/storage_strategy.py +202 -0
- contextsynapse/config/templates/competitive_intel.yaml +39 -0
- contextsynapse/config/templates/geopolitical_risk.yaml +46 -0
- contextsynapse/config/templates/market_analysis.yaml +58 -0
- contextsynapse/connectors/__init__.py +15 -0
- contextsynapse/connectors/api_router.py +121 -0
- contextsynapse/connectors/base.py +357 -0
- contextsynapse/connectors/financial/audio.py +170 -0
- contextsynapse/connectors/financial/youtube.py +272 -0
- contextsynapse/connectors/github.py +164 -0
- contextsynapse/connectors/jira.py +120 -0
- contextsynapse/connectors/kafka_connector.py +124 -0
- contextsynapse/connectors/pipeline_connector.py +747 -0
- contextsynapse/connectors/registry.py +116 -0
- contextsynapse/connectors/scheduler.py +87 -0
- contextsynapse/connectors/universal.py +360 -0
- contextsynapse/context/__init__.py +96 -0
- contextsynapse/context/__main__.py +4 -0
- contextsynapse/context/acl.py +125 -0
- contextsynapse/context/agent_card.py +201 -0
- contextsynapse/context/agent_memory.py +814 -0
- contextsynapse/context/agents.py +472 -0
- contextsynapse/context/agents_redis.py +391 -0
- contextsynapse/context/assembled.py +204 -0
- contextsynapse/context/attribution.py +113 -0
- contextsynapse/context/blob.py +184 -0
- contextsynapse/context/boundaries.py +187 -0
- contextsynapse/context/boundary.py +507 -0
- contextsynapse/context/bundle.py +155 -0
- contextsynapse/context/cli.py +519 -0
- contextsynapse/context/collaboration.py +165 -0
- contextsynapse/context/compiler.py +604 -0
- contextsynapse/context/composite.py +536 -0
- contextsynapse/context/compression.py +147 -0
- contextsynapse/context/context_manager.py +888 -0
- contextsynapse/context/context_schema.py +90 -0
- contextsynapse/context/context_schema.yaml +105 -0
- contextsynapse/context/context_units.py +891 -0
- contextsynapse/context/conversation.py +488 -0
- contextsynapse/context/dedup.py +207 -0
- contextsynapse/context/document_processor.py +698 -0
- contextsynapse/context/embedding_hooks.py +338 -0
- contextsynapse/context/execution_schema.py +274 -0
- contextsynapse/context/fan_out.py +138 -0
- contextsynapse/context/frozen.py +80 -0
- contextsynapse/context/gravity.py +227 -0
- contextsynapse/context/hub.py +1401 -0
- contextsynapse/context/ingest.py +391 -0
- contextsynapse/context/injection.py +226 -0
- contextsynapse/context/interaction_graphifier.py +562 -0
- contextsynapse/context/layers.py +574 -0
- contextsynapse/context/projection.py +1195 -0
- contextsynapse/context/promotion.py +634 -0
- contextsynapse/context/proof.py +131 -0
- contextsynapse/context/propagation.py +577 -0
- contextsynapse/context/pubsub.py +135 -0
- contextsynapse/context/quality.py +1054 -0
- contextsynapse/context/quality_tracker.py +270 -0
- contextsynapse/context/scoping.py +370 -0
- contextsynapse/context/seed.py +992 -0
- contextsynapse/context/session.py +1142 -0
- contextsynapse/context/session_graph.py +593 -0
- contextsynapse/context/session_memory.py +309 -0
- contextsynapse/context/session_resolver.py +111 -0
- contextsynapse/context/skill_learning.py +237 -0
- contextsynapse/context/smart_budget.py +129 -0
- contextsynapse/context/source_policy.py +242 -0
- contextsynapse/context/store_factory.py +53 -0
- contextsynapse/context/sync.py +648 -0
- contextsynapse/context/token.py +236 -0
- contextsynapse/context/vector_integration.py +297 -0
- contextsynapse/context/webhooks.py +194 -0
- contextsynapse/context/working_memory.py +224 -0
- contextsynapse/core/__init__.py +18 -0
- contextsynapse/core/archiver.py +237 -0
- contextsynapse/core/change_stream.py +282 -0
- contextsynapse/core/checkpoint.py +310 -0
- contextsynapse/core/cloud_storage.py +387 -0
- contextsynapse/core/context_state.py +651 -0
- contextsynapse/core/cron.py +324 -0
- contextsynapse/core/datasource.py +643 -0
- contextsynapse/core/db.py +289 -0
- contextsynapse/core/federation.py +283 -0
- contextsynapse/core/graph_coordinator.py +201 -0
- contextsynapse/core/graph_intelligence.py +388 -0
- contextsynapse/core/graph_structures.py +106 -0
- contextsynapse/core/graph_sync.py +157 -0
- contextsynapse/core/hybrid_graph_storage.py +2998 -0
- contextsynapse/core/lifecycle.py +691 -0
- contextsynapse/core/multiworker.py +378 -0
- contextsynapse/core/pruning.py +312 -0
- contextsynapse/core/redis_registry.py +524 -0
- contextsynapse/core/registry.py +1043 -0
- contextsynapse/core/registry_factory.py +51 -0
- contextsynapse/core/registry_metadata.py +294 -0
- contextsynapse/core/replay.py +354 -0
- contextsynapse/core/replication.py +339 -0
- contextsynapse/core/time_travel.py +260 -0
- contextsynapse/core/write_behind.py +152 -0
- contextsynapse/core/write_context.py +39 -0
- contextsynapse/db/__init__.py +1 -0
- contextsynapse/db/postgres.py +267 -0
- contextsynapse/db/profile.py +236 -0
- contextsynapse/db/rules.py +328 -0
- contextsynapse/demo/__init__.py +0 -0
- contextsynapse/demo/report.py +147 -0
- contextsynapse/demo/runner.py +520 -0
- contextsynapse/demo/scenario.py +165 -0
- contextsynapse/demo/session_demo.py +280 -0
- contextsynapse/demo/session_report.py +143 -0
- contextsynapse/demo/universal_runner.py +257 -0
- contextsynapse/engine/__init__.py +15 -0
- contextsynapse/engine/core.py +555 -0
- contextsynapse/extraction/__init__.py +69 -0
- contextsynapse/extraction/cli.py +274 -0
- contextsynapse/extraction/config.py +107 -0
- contextsynapse/extraction/examples/example_usage.py +118 -0
- contextsynapse/extraction/examples/sample_manifest.json +53 -0
- contextsynapse/extraction/examples/sample_normalized.json +185 -0
- contextsynapse/extraction/extract_engine.py +440 -0
- contextsynapse/extraction/extractors/__init__.py +37 -0
- contextsynapse/extraction/extractors/chat_export_extractor.py +189 -0
- contextsynapse/extraction/extractors/csv_extractor.py +83 -0
- contextsynapse/extraction/extractors/docx_extractor.py +129 -0
- contextsynapse/extraction/extractors/excel_extractor.py +152 -0
- contextsynapse/extraction/extractors/html_extractor.py +146 -0
- contextsynapse/extraction/extractors/pdf_extractor.py +635 -0
- contextsynapse/extraction/extractors/pdf_extractor_layoutparser.py +408 -0
- contextsynapse/extraction/extractors/register_extractors.py +63 -0
- contextsynapse/extraction/extractors/text_extractor.py +65 -0
- contextsynapse/extraction/extractors/txt_extractor.py +120 -0
- contextsynapse/extraction/extractors/website_extractor.py +246 -0
- contextsynapse/extraction/fact_extractor.py +236 -0
- contextsynapse/extraction/file_manager.py +353 -0
- contextsynapse/extraction/hierarchy.py +239 -0
- contextsynapse/extraction/id_generator.py +450 -0
- contextsynapse/extraction/layout_detector.py +409 -0
- contextsynapse/extraction/llm_entity_extractor.py +301 -0
- contextsynapse/extraction/normalized_store.py +480 -0
- contextsynapse/extraction/normalizer.py +594 -0
- contextsynapse/extraction/parquet_writer.py +598 -0
- contextsynapse/extraction/post_processors/__init__.py +40 -0
- contextsynapse/extraction/post_processors/column_reorganizer.py +507 -0
- contextsynapse/extraction/ray_normalization.py +233 -0
- contextsynapse/extraction/ray_runner.py +207 -0
- contextsynapse/extraction/redis_schema_registry.py +222 -0
- contextsynapse/extraction/registry.py +204 -0
- contextsynapse/extraction/schema_loader.py +663 -0
- contextsynapse/extraction/schema_registry.py +230 -0
- contextsynapse/extraction/semantic_normalizer.py +245 -0
- contextsynapse/extraction/version_manager.py +314 -0
- contextsynapse/gateway/__init__.py +24 -0
- contextsynapse/gateway/action_emitter.py +212 -0
- contextsynapse/gateway/cost_tracker.py +508 -0
- contextsynapse/gateway/gateway.py +377 -0
- contextsynapse/gateway/policy.py +303 -0
- contextsynapse/governance/__init__.py +13 -0
- contextsynapse/governance/layer.py +513 -0
- contextsynapse/ingestion/__init__.py +15 -0
- contextsynapse/ingestion/amplifier.py +288 -0
- contextsynapse/ingestion/async_ingest.py +463 -0
- contextsynapse/ingestion/chunker.py +303 -0
- contextsynapse/ingestion/chunking.py +58 -0
- contextsynapse/ingestion/cleaner.py +190 -0
- contextsynapse/ingestion/cleanse.py +252 -0
- contextsynapse/ingestion/connectors/__init__.py +38 -0
- contextsynapse/ingestion/connectors/base.py +300 -0
- contextsynapse/ingestion/connectors/chatgpt_memory.py +247 -0
- contextsynapse/ingestion/connectors/claude_memory.py +274 -0
- contextsynapse/ingestion/connectors/sap_ingestor.py +208 -0
- contextsynapse/ingestion/content_detector.py +80 -0
- contextsynapse/ingestion/content_monitor.py +502 -0
- contextsynapse/ingestion/dedup.py +106 -0
- contextsynapse/ingestion/feed.py +640 -0
- contextsynapse/ingestion/filters.py +461 -0
- contextsynapse/ingestion/graph_builder.py +705 -0
- contextsynapse/ingestion/input_classifier.py +307 -0
- contextsynapse/ingestion/job_manager.py +571 -0
- contextsynapse/ingestion/llm_extractor.py +610 -0
- contextsynapse/ingestion/orchestrator.py +116 -0
- contextsynapse/ingestion/parsers/__init__.py +19 -0
- contextsynapse/ingestion/parsers/graph_parser_registry.py +103 -0
- contextsynapse/ingestion/parsers/graphml_parser.py +141 -0
- contextsynapse/ingestion/parsers/jsonld_parser.py +171 -0
- contextsynapse/ingestion/parsers/rdf_parser.py +150 -0
- contextsynapse/ingestion/pipeline_context.py +228 -0
- contextsynapse/ingestion/pipeline_store.py +1195 -0
- contextsynapse/ingestion/queue.py +133 -0
- contextsynapse/ingestion/reprocess.py +179 -0
- contextsynapse/ingestion/scenario_router.py +224 -0
- contextsynapse/ingestion/schema_extractor.py +759 -0
- contextsynapse/ingestion/schema_validator.py +274 -0
- contextsynapse/ingestion/sdlc_ingest.py +87 -0
- contextsynapse/ingestion/smart_ingest.py +2451 -0
- contextsynapse/ingestion/source_connector.py +171 -0
- contextsynapse/ingestion/stage_executor.py +2095 -0
- contextsynapse/ingestion/stages/__init__.py +9 -0
- contextsynapse/ingestion/stages/tabular_to_graph.py +289 -0
- contextsynapse/ingestion/strategies.py +386 -0
- contextsynapse/ingestion/universal/__init__.py +4 -0
- contextsynapse/ingestion/universal/_operator_registry.py +59 -0
- contextsynapse/ingestion/universal/ingest_content.py +32 -0
- contextsynapse/ingestion/universal/operators/__init__.py +1 -0
- contextsynapse/ingestion/universal/operators/base.py +19 -0
- contextsynapse/ingestion/universal/operators/build_edges.py +75 -0
- contextsynapse/ingestion/universal/operators/cluster_topics.py +397 -0
- contextsynapse/ingestion/universal/operators/deduplicate.py +45 -0
- contextsynapse/ingestion/universal/operators/detect_signals.py +90 -0
- contextsynapse/ingestion/universal/operators/embed.py +80 -0
- contextsynapse/ingestion/universal/operators/extract_entities.py +253 -0
- contextsynapse/ingestion/universal/operators/extract_preferences.py +117 -0
- contextsynapse/ingestion/universal/operators/filter_content.py +69 -0
- contextsynapse/ingestion/universal/operators/index_bm25.py +52 -0
- contextsynapse/ingestion/universal/operators/infer_domains.py +69 -0
- contextsynapse/ingestion/universal/operators/link_cross_reference.py +210 -0
- contextsynapse/ingestion/universal/operators/parse_records.py +66 -0
- contextsynapse/ingestion/universal/operators/resolve_entities.py +261 -0
- contextsynapse/ingestion/universal/operators/scanners/__init__.py +16 -0
- contextsynapse/ingestion/universal/operators/scanners/edges.py +173 -0
- contextsynapse/ingestion/universal/operators/scanners/git.py +187 -0
- contextsynapse/ingestion/universal/operators/scanners/github.py +135 -0
- contextsynapse/ingestion/universal/operators/scanners/github_api.py +509 -0
- contextsynapse/ingestion/universal/operators/scanners/helpers.py +67 -0
- contextsynapse/ingestion/universal/operators/scanners/jira.py +65 -0
- contextsynapse/ingestion/universal/operators/scanners/llm_enrichment.py +82 -0
- contextsynapse/ingestion/universal/operators/scanners/repo.py +149 -0
- contextsynapse/ingestion/universal/operators/sdlc_scan.py +213 -0
- contextsynapse/ingestion/universal/operators/spec_scanner.py +149 -0
- contextsynapse/ingestion/universal/operators/store_documents.py +72 -0
- contextsynapse/ingestion/universal/operators/synthesize_cu.py +180 -0
- contextsynapse/ingestion/universal/operators/validate_gate.py +65 -0
- contextsynapse/ingestion/universal/parsers/__init__.py +5 -0
- contextsynapse/ingestion/universal/parsers/record_parser.py +203 -0
- contextsynapse/ingestion/universal/parsers/turn_parser.py +329 -0
- contextsynapse/ingestion/universal/shared_signals.py +161 -0
- contextsynapse/ingestion/universal/stage_executor.py +219 -0
- contextsynapse/ingestion/web_crawler.py +265 -0
- contextsynapse/intelligence/__init__.py +53 -0
- contextsynapse/intelligence/alerts.py +198 -0
- contextsynapse/intelligence/collector.py +655 -0
- contextsynapse/intelligence/comparison.py +198 -0
- contextsynapse/intelligence/config.py +56 -0
- contextsynapse/intelligence/conflict_detector.py +182 -0
- contextsynapse/intelligence/context_gaps.py +192 -0
- contextsynapse/intelligence/context_radar.py +135 -0
- contextsynapse/intelligence/correlation_engine.py +416 -0
- contextsynapse/intelligence/correlation_template.py +120 -0
- contextsynapse/intelligence/dedup.py +296 -0
- contextsynapse/intelligence/event_bus.py +91 -0
- contextsynapse/intelligence/feedback_loop.py +101 -0
- contextsynapse/intelligence/freshness.py +307 -0
- contextsynapse/intelligence/fundamental_feed.py +551 -0
- contextsynapse/intelligence/geo_data.py +170 -0
- contextsynapse/intelligence/geo_reference.py +112 -0
- contextsynapse/intelligence/graph_fusion.py +465 -0
- contextsynapse/intelligence/heuristics.py +176 -0
- contextsynapse/intelligence/impact_tracker.py +336 -0
- contextsynapse/intelligence/macro_indicators.py +319 -0
- contextsynapse/intelligence/ocr.py +104 -0
- contextsynapse/intelligence/persistence.py +267 -0
- contextsynapse/intelligence/pipeline_health.py +130 -0
- contextsynapse/intelligence/price_feed.py +221 -0
- contextsynapse/intelligence/rate_limiter.py +130 -0
- contextsynapse/intelligence/reactive.py +411 -0
- contextsynapse/intelligence/runtime_context.py +390 -0
- contextsynapse/intelligence/sentiment_decay.py +426 -0
- contextsynapse/intelligence/session.py +277 -0
- contextsynapse/intelligence/signal_hierarchy.py +438 -0
- contextsynapse/intelligence/signals.py +185 -0
- contextsynapse/intelligence/source_watcher.py +132 -0
- contextsynapse/intelligence/table_enricher.py +256 -0
- contextsynapse/intelligence/tagger.py +319 -0
- contextsynapse/intelligence/timeseries.py +209 -0
- contextsynapse/intelligence/tracker.py +318 -0
- contextsynapse/intelligence/watchdog.py +392 -0
- contextsynapse/intelligence/watchdog_manager.py +263 -0
- contextsynapse/intelligence/webhook_receiver.py +165 -0
- contextsynapse/intelligence/weight_learner.py +234 -0
- contextsynapse/llm/__init__.py +8 -0
- contextsynapse/llm/client.py +348 -0
- contextsynapse/llm/rate_limiter.py +166 -0
- contextsynapse/logging_config.py +2 -0
- contextsynapse/marketplace/__init__.py +7 -0
- contextsynapse/marketplace/registry.py +173 -0
- contextsynapse/mcp/__init__.py +11 -0
- contextsynapse/mcp/__main__.py +4 -0
- contextsynapse/mcp/auth_middleware.py +212 -0
- contextsynapse/mcp/connection_pool.py +128 -0
- contextsynapse/mcp/server.py +986 -0
- contextsynapse/mcp/session_context.py +67 -0
- contextsynapse/memory/__init__.py +34 -0
- contextsynapse/memory/engine.py +475 -0
- contextsynapse/memory/temporal.py +327 -0
- contextsynapse/metadata/__init__.py +12 -0
- contextsynapse/metadata/metadata_db.py +272 -0
- contextsynapse/metadata/metadata_query_tool.py +300 -0
- contextsynapse/metadata/metadata_tracker.py +425 -0
- contextsynapse/models/__init__.py +27 -0
- contextsynapse/models/embedding_service.py +360 -0
- contextsynapse/models/model_registry.py +228 -0
- contextsynapse/multiworker.py +2 -0
- contextsynapse/pipelines/__init__.py +1 -0
- contextsynapse/pipelines/executor.py +851 -0
- contextsynapse/pipelines/filters.py +189 -0
- contextsynapse/pipelines/models.py +176 -0
- contextsynapse/pipelines/scheduler.py +268 -0
- contextsynapse/playground/__init__.py +1 -0
- contextsynapse/playground/templates.py +85 -0
- contextsynapse/plugins/__init__.py +43 -0
- contextsynapse/plugins/api.py +66 -0
- contextsynapse/plugins/base.py +350 -0
- contextsynapse/plugins/domain_interface.py +508 -0
- contextsynapse/plugins/domain_loader.py +241 -0
- contextsynapse/plugins/loader.py +148 -0
- contextsynapse/plugins/registry.py +265 -0
- contextsynapse/project/__init__.py +11 -0
- contextsynapse/project/code_context.py +807 -0
- contextsynapse/project/connectors/__init__.py +4 -0
- contextsynapse/project/connectors/github_connector.py +113 -0
- contextsynapse/project/connectors/jira_connector.py +160 -0
- contextsynapse/project/graph.py +1023 -0
- contextsynapse/project/project_context.py +545 -0
- contextsynapse/project/scanner_registry.py +61 -0
- contextsynapse/project/schema_manager.py +322 -0
- contextsynapse/project/schemas/knowledge.yaml +59 -0
- contextsynapse/project/schemas/sdlc.yaml +369 -0
- contextsynapse/project/sdlc_schema.py +172 -0
- contextsynapse/project/spec_parser.py +85 -0
- contextsynapse/project/task_lock.py +181 -0
- contextsynapse/project/task_stream.py +203 -0
- contextsynapse/project/team_tools.py +537 -0
- contextsynapse/py.typed +0 -0
- contextsynapse/realtime/__init__.py +6 -0
- contextsynapse/realtime/collaboration.py +147 -0
- contextsynapse/rules/__init__.py +10 -0
- contextsynapse/rules/engine.py +116 -0
- contextsynapse/rules/evaluators/__init__.py +6 -0
- contextsynapse/rules/evaluators/aggregate.py +205 -0
- contextsynapse/rules/evaluators/custom.py +170 -0
- contextsynapse/rules/evaluators/pre_action.py +125 -0
- contextsynapse/rules/evaluators/temporal.py +129 -0
- contextsynapse/rules/loader.py +52 -0
- contextsynapse/rules/models.py +124 -0
- contextsynapse/rules/universal.py +393 -0
- contextsynapse/scheduler.py +229 -0
- contextsynapse/schema/__init__.py +42 -0
- contextsynapse/schema/compiler.py +252 -0
- contextsynapse/schema/composer.py +158 -0
- contextsynapse/schema/dedup.py +73 -0
- contextsynapse/schema/derivation.py +172 -0
- contextsynapse/schema/sdl.py +326 -0
- contextsynapse/schema/validation_gate.py +124 -0
- contextsynapse/sdk/__init__.py +63 -0
- contextsynapse/sdk/agent.py +214 -0
- contextsynapse/sdk/client.py +269 -0
- contextsynapse/sdk/exceptions.py +23 -0
- contextsynapse/sdk/models.py +78 -0
- contextsynapse/sdk/session.py +408 -0
- contextsynapse/sdk/tracked.py +218 -0
- contextsynapse/sdk/worker.py +266 -0
- contextsynapse/sdk/workspace_client.py +98 -0
- contextsynapse/sdk/ws.py +41 -0
- contextsynapse/search/__init__.py +20 -0
- contextsynapse/search/embedding_cache.py +140 -0
- contextsynapse/search/enhanced_search.py +152 -0
- contextsynapse/search/fulltext.py +130 -0
- contextsynapse/search/graph_search.py +634 -0
- contextsynapse/search/lmdb_index.py +1218 -0
- contextsynapse/search/nl_to_aiql.py +199 -0
- contextsynapse/search/rag.py +1346 -0
- contextsynapse/search/rag_cache.py +167 -0
- contextsynapse/search/reasoning_chain.py +231 -0
- contextsynapse/search/redis_search.py +207 -0
- contextsynapse/search/retrieval_quality.py +314 -0
- contextsynapse/search/semantic_query.py +455 -0
- contextsynapse/search/text_resolver.py +159 -0
- contextsynapse/search/whoosh_search.py +373 -0
- contextsynapse/security/__init__.py +17 -0
- contextsynapse/security/audit.py +297 -0
- contextsynapse/security/audit_logger.py +97 -0
- contextsynapse/security/audit_trail.py +270 -0
- contextsynapse/security/auth_unified.py +284 -0
- contextsynapse/security/auto_tagger.py +230 -0
- contextsynapse/security/data_security.py +218 -0
- contextsynapse/security/encryption.py +207 -0
- contextsynapse/security/identity.py +447 -0
- contextsynapse/security/jwt_identity.py +59 -0
- contextsynapse/security/middleware.py +336 -0
- contextsynapse/security/pii.py +233 -0
- contextsynapse/security/rbac.py +96 -0
- contextsynapse/security/rls.py +205 -0
- contextsynapse/security/sanitize.py +233 -0
- contextsynapse/security/scoped_encryption.py +131 -0
- contextsynapse/security/tenant.py +401 -0
- contextsynapse/shield/__init__.py +21 -0
- contextsynapse/shield/anomaly.py +56 -0
- contextsynapse/shield/permissions.py +99 -0
- contextsynapse/shield/profile.py +103 -0
- contextsynapse/shield/shield.py +108 -0
- contextsynapse/shield/trust_engine.py +69 -0
- contextsynapse/skills/__init__.py +14 -0
- contextsynapse/skills/engine.py +233 -0
- contextsynapse/skills/loader.py +59 -0
- contextsynapse/skills/models.py +139 -0
- contextsynapse/storage/__init__.py +7 -0
- contextsynapse/storage/cache.py +208 -0
- contextsynapse/storage/columnar_store_v2.py +178 -0
- contextsynapse/storage/csr_graph_storage.py +587 -0
- contextsynapse/storage/csr_store.py +388 -0
- contextsynapse/storage/document_store.py +491 -0
- contextsynapse/storage/hnsw_index.py +294 -0
- contextsynapse/storage/hybrid_store_v2.py +278 -0
- contextsynapse/storage/lmdb_graph_storage.py +366 -0
- contextsynapse/storage/migration.py +279 -0
- contextsynapse/storage/namespace_store.py +346 -0
- contextsynapse/storage/pure_graph_storage.py +185 -0
- contextsynapse/storage/redis_graph_adapter.py +186 -0
- contextsynapse/storage/redis_graph_storage.py +375 -0
- contextsynapse/storage/router/__init__.py +29 -0
- contextsynapse/storage/router/duckdb_store.py +398 -0
- contextsynapse/storage/router/factory.py +83 -0
- contextsynapse/storage/router/interface.py +125 -0
- contextsynapse/storage/router/postgres_store.py +339 -0
- contextsynapse/storage/router/resolver.py +192 -0
- contextsynapse/storage/router/router.py +300 -0
- contextsynapse/storage/single_file_storage.py +391 -0
- contextsynapse/storage/storage_manager.py +214 -0
- contextsynapse/storage/storage_query_optimizer.py +314 -0
- contextsynapse/storage/wal.py +359 -0
- contextsynapse/temporal/__init__.py +0 -0
- contextsynapse/temporal/storage.py +106 -0
- contextsynapse/tests/__init__.py +11 -0
- contextsynapse/tests/regression/__init__.py +1 -0
- contextsynapse/tests/regression/add_metadata.py +395 -0
- contextsynapse/tests/regression/add_skip_flags.py +85 -0
- contextsynapse/tests/regression/cases/test_basic_create_node.yaml +20 -0
- contextsynapse/tests/regression/cases/test_basic_select.yaml +21 -0
- contextsynapse/tests/regression/cases/test_blockchain_audit_trail.yaml +37 -0
- contextsynapse/tests/regression/cases/test_blockchain_audit_trail_filtered.yaml +39 -0
- contextsynapse/tests/regression/cases/test_blockchain_blocks_range.yaml +39 -0
- contextsynapse/tests/regression/cases/test_blockchain_get_block.yaml +35 -0
- contextsynapse/tests/regression/cases/test_blockchain_latest_block.yaml +35 -0
- contextsynapse/tests/regression/cases/test_blockchain_length.yaml +37 -0
- contextsynapse/tests/regression/cases/test_blockchain_merkle_root.yaml +37 -0
- contextsynapse/tests/regression/cases/test_blockchain_verify.yaml +35 -0
- contextsynapse/tests/regression/cases/test_blockchain_verify_block.yaml +35 -0
- contextsynapse/tests/regression/cases/test_create_edge.yaml +28 -0
- contextsynapse/tests/regression/cases/test_delete_edge_basic.yaml +29 -0
- contextsynapse/tests/regression/cases/test_delete_node_basic.yaml +29 -0
- contextsynapse/tests/regression/cases/test_evaluation_mrr.yaml +40 -0
- contextsynapse/tests/regression/cases/test_evaluation_ndcg.yaml +40 -0
- contextsynapse/tests/regression/cases/test_evaluation_precision.yaml +40 -0
- contextsynapse/tests/regression/cases/test_evaluation_recall.yaml +40 -0
- contextsynapse/tests/regression/cases/test_group_by_basic.yaml +56 -0
- contextsynapse/tests/regression/cases/test_match_node_basic.yaml +29 -0
- contextsynapse/tests/regression/cases/test_match_node_with_order_limit.yaml +31 -0
- contextsynapse/tests/regression/cases/test_namespace_switch.yaml +18 -0
- contextsynapse/tests/regression/cases/test_pipeline_cascaded.yaml +61 -0
- contextsynapse/tests/regression/cases/test_pipeline_fixed_size_chunking.yaml +48 -0
- contextsynapse/tests/regression/cases/test_pipeline_gpt4_chunking.yaml +54 -0
- contextsynapse/tests/regression/cases/test_pipeline_hybrid_indexing.yaml +57 -0
- contextsynapse/tests/regression/cases/test_pipeline_large_embedding.yaml +52 -0
- contextsynapse/tests/regression/cases/test_pipeline_paragraph_chunking.yaml +49 -0
- contextsynapse/tests/regression/cases/test_pipeline_recursive_chunking.yaml +47 -0
- contextsynapse/tests/regression/cases/test_pipeline_run_pipeline.yaml +56 -0
- contextsynapse/tests/regression/cases/test_pipeline_section_based_chunking.yaml +53 -0
- contextsynapse/tests/regression/cases/test_pipeline_semantic_chunking.yaml +51 -0
- contextsynapse/tests/regression/cases/test_pipeline_with_entity_extraction.yaml +57 -0
- contextsynapse/tests/regression/cases/test_pipeline_with_relationships.yaml +62 -0
- contextsynapse/tests/regression/cases/test_rag_evaluation.yaml +38 -0
- contextsynapse/tests/regression/cases/test_rag_graph_bfs_with_eval.yaml +48 -0
- contextsynapse/tests/regression/cases/test_rag_graph_centrality.yaml +40 -0
- contextsynapse/tests/regression/cases/test_rag_graph_centrality_eval.yaml +35 -0
- contextsynapse/tests/regression/cases/test_rag_graph_community.yaml +40 -0
- contextsynapse/tests/regression/cases/test_rag_graph_community_eval.yaml +35 -0
- contextsynapse/tests/regression/cases/test_rag_graph_dfs_with_eval.yaml +35 -0
- contextsynapse/tests/regression/cases/test_rag_graph_multi_hop_eval.yaml +35 -0
- contextsynapse/tests/regression/cases/test_rag_graph_path.yaml +40 -0
- contextsynapse/tests/regression/cases/test_rag_graph_search_basic.yaml +38 -0
- contextsynapse/tests/regression/cases/test_rag_graph_search_bfs.yaml +52 -0
- contextsynapse/tests/regression/cases/test_rag_graph_search_dfs.yaml +41 -0
- contextsynapse/tests/regression/cases/test_rag_graph_search_multi_hop.yaml +43 -0
- contextsynapse/tests/regression/cases/test_rag_graph_search_shortest_path.yaml +41 -0
- contextsynapse/tests/regression/cases/test_rag_graph_search_weighted.yaml +43 -0
- contextsynapse/tests/regression/cases/test_rag_graph_shortest_path_eval.yaml +35 -0
- contextsynapse/tests/regression/cases/test_rag_graph_traversal.yaml +30 -0
- contextsynapse/tests/regression/cases/test_rag_graph_weighted_eval.yaml +35 -0
- contextsynapse/tests/regression/cases/test_rag_hybrid_search.yaml +33 -0
- contextsynapse/tests/regression/cases/test_rag_hybrid_with_prompt_embedding.yaml +44 -0
- contextsynapse/tests/regression/cases/test_rag_keyword_search.yaml +32 -0
- contextsynapse/tests/regression/cases/test_rag_multi_hop_graph.yaml +32 -0
- contextsynapse/tests/regression/cases/test_rag_path_based.yaml +34 -0
- contextsynapse/tests/regression/cases/test_rag_profile_based.yaml +32 -0
- contextsynapse/tests/regression/cases/test_rag_vector_search.yaml +32 -0
- contextsynapse/tests/regression/cases/test_rag_weighted_graph.yaml +34 -0
- contextsynapse/tests/regression/cases/test_rag_weighted_hybrid.yaml +31 -0
- contextsynapse/tests/regression/cases/test_semantic_boundary_chunking.yaml +57 -0
- contextsynapse/tests/regression/cases/test_semantic_boundary_detection.yaml +33 -0
- contextsynapse/tests/regression/cases/test_semantic_hash_function.yaml +30 -0
- contextsynapse/tests/regression/cases/test_semantic_hash_with_model.yaml +31 -0
- contextsynapse/tests/regression/cases/test_tcs_ingestion_fixed_size.yaml +44 -0
- contextsynapse/tests/regression/cases/test_tcs_ingestion_recursive.yaml +46 -0
- contextsynapse/tests/regression/cases/test_tcs_ingestion_section.yaml +46 -0
- contextsynapse/tests/regression/cases/test_tcs_ingestion_semantic.yaml +44 -0
- contextsynapse/tests/regression/cases/test_time_travel_as_of.yaml +35 -0
- contextsynapse/tests/regression/cases/test_time_travel_at_commit.yaml +33 -0
- contextsynapse/tests/regression/cases/test_time_travel_at_timestamp.yaml +33 -0
- contextsynapse/tests/regression/cases/test_time_travel_for_system_time_all.yaml +37 -0
- contextsynapse/tests/regression/cases/test_time_travel_versions_between.yaml +39 -0
- contextsynapse/tests/regression/cases/test_traverse_basic.yaml +31 -0
- contextsynapse/tests/regression/cases/test_traverse_max_depth.yaml +34 -0
- contextsynapse/tests/regression/cases/test_update_edge_basic.yaml +29 -0
- contextsynapse/tests/regression/evaluation_metrics.py +227 -0
- contextsynapse/tests/regression/regression_runner.py +1081 -0
- contextsynapse/tests/regression/setup_regression_data.py +212 -0
- contextsynapse/tests/regression/test_basic_only.py +84 -0
- contextsynapse/tests/regression/test_namespace_creation.py +29 -0
- contextsynapse/tests/regression/test_single_fix.py +61 -0
- contextsynapse/tests/regression/test_single_query.py +20 -0
- contextsynapse/tests/regression/validate_test_syntax.py +179 -0
- contextsynapse/tests/test_advanced_features.py +347 -0
- contextsynapse/tests/test_aiql_summary.py +283 -0
- contextsynapse/tests/test_all_aiql_queries.py +948 -0
- contextsynapse/tests/test_all_create_queries.py +213 -0
- contextsynapse/tests/test_api_vs_native.py +280 -0
- contextsynapse/tests/test_chunking_dedup.py +199 -0
- contextsynapse/tests/test_connectors.py +171 -0
- contextsynapse/tests/test_edges_and_match.py +202 -0
- contextsynapse/tests/test_extraction_pipeline_integration.py +581 -0
- contextsynapse/tests/test_name_property.py +224 -0
- contextsynapse/tests/test_node_edge_metadata.py +250 -0
- contextsynapse/tests/test_playground_conn.py +114 -0
- contextsynapse/tests/test_query_cache.py +213 -0
- contextsynapse/tests/test_source_policy_auth.py +348 -0
- contextsynapse/tests/test_temporal_and_domain.py +224 -0
- contextsynapse/tests/test_time_travel_e2e.py +571 -0
- contextsynapse/tests/test_traversal_and_aggregation.py +275 -0
- contextsynapse/tools/__init__.py +36 -0
- contextsynapse/tools/a2a_tools.py +106 -0
- contextsynapse/tools/cognition_tools.py +118 -0
- contextsynapse/tools/context.py +905 -0
- contextsynapse/tools/formatting.py +92 -0
- contextsynapse/tools/gateway_tools.py +186 -0
- contextsynapse/tools/graph.py +1700 -0
- contextsynapse/tools/intelligence.py +235 -0
- contextsynapse/tools/memory.py +118 -0
- contextsynapse/tools/registry.py +373 -0
- contextsynapse/tools/shield_tools.py +159 -0
- contextsynapse/tools/tasks.py +341 -0
- contextsynapse/tools/workspace_tools.py +124 -0
- contextsynapse/utils/__init__.py +15 -0
- contextsynapse/utils/config_loader.py +198 -0
- contextsynapse/utils/env_loader.py +156 -0
- contextsynapse/utils/file_utils.py +288 -0
- contextsynapse/utils/logging_optimizer.py +119 -0
- contextsynapse/vector/__init__.py +30 -0
- contextsynapse/vector/chroma_vector_store.py +152 -0
- contextsynapse/vector/custom_vector_store.py +537 -0
- contextsynapse/vector/faiss_vector_store.py +296 -0
- contextsynapse/vector/qdrant_vector_store.py +163 -0
- contextsynapse/vector/vector_db.py +953 -0
- contextsynapse/vector/vector_db_manager.py +197 -0
- contextsynapse/workspace/__init__.py +27 -0
- contextsynapse/workspace/base.py +94 -0
- contextsynapse/workspace/git.py +228 -0
- contextsynapse/workspace/github.py +78 -0
- contextsynapse/workspace/local.py +73 -0
- contextsynapse/workspace/tools.py +222 -0
- contextsynapse-1.0.0.dist-info/METADATA +847 -0
- contextsynapse-1.0.0.dist-info/RECORD +776 -0
- contextsynapse-1.0.0.dist-info/WHEEL +5 -0
- contextsynapse-1.0.0.dist-info/entry_points.txt +4 -0
- contextsynapse-1.0.0.dist-info/licenses/LICENSE +200 -0
- contextsynapse-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"""RLS — Row-Level Security enforcement.
|
|
2
|
+
|
|
3
|
+
Filters query results based on user's scoped_ids.
|
|
4
|
+
Users only see resources they're authorized for.
|
|
5
|
+
|
|
6
|
+
Global access roles (admin, compliance_officer) bypass RLS.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import logging
|
|
11
|
+
from typing import Any, Dict, List, Optional, Set
|
|
12
|
+
|
|
13
|
+
from fastapi import Depends
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# Roles that bypass all row-level filtering
|
|
19
|
+
GLOBAL_ACCESS_ROLES: Set[str] = {"admin", "compliance_officer"}
|
|
20
|
+
|
|
21
|
+
# Roles that can see all clients (in addition to global)
|
|
22
|
+
_CLIENT_GLOBAL_ROLES: Set[str] = {"admin", "compliance_officer", "operations"}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class RLSFilter:
|
|
26
|
+
"""Row-level security filter scoped to a single authenticated user.
|
|
27
|
+
|
|
28
|
+
Instantiate per-request from the JWT claims and use the filter_*
|
|
29
|
+
methods to strip unauthorised rows before returning API responses.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def __init__(
|
|
33
|
+
self,
|
|
34
|
+
user_id: str,
|
|
35
|
+
role: str,
|
|
36
|
+
scoped_ids: Optional[List[str]],
|
|
37
|
+
tenant_id: str,
|
|
38
|
+
):
|
|
39
|
+
self.user_id = user_id
|
|
40
|
+
self.role = role
|
|
41
|
+
self.scoped_ids: List[str] = scoped_ids or []
|
|
42
|
+
self.tenant_id = tenant_id
|
|
43
|
+
self._scoped_set: Set[str] = set(self.scoped_ids)
|
|
44
|
+
self._is_global = role in GLOBAL_ACCESS_ROLES
|
|
45
|
+
|
|
46
|
+
# ------------------------------------------------------------------
|
|
47
|
+
# Generic helper
|
|
48
|
+
# ------------------------------------------------------------------
|
|
49
|
+
|
|
50
|
+
def apply_filter(
|
|
51
|
+
self,
|
|
52
|
+
items: List[dict],
|
|
53
|
+
id_field: str,
|
|
54
|
+
allowed_ids: Optional[Set[str]] = None,
|
|
55
|
+
) -> List[dict]:
|
|
56
|
+
"""Filter a list of dicts, keeping only those whose *id_field*
|
|
57
|
+
value is in *allowed_ids*.
|
|
58
|
+
|
|
59
|
+
If *allowed_ids* is None the user's scoped_ids are used.
|
|
60
|
+
"""
|
|
61
|
+
ids = allowed_ids if allowed_ids is not None else self._scoped_set
|
|
62
|
+
return [item for item in items if item.get(id_field) in ids]
|
|
63
|
+
|
|
64
|
+
# ------------------------------------------------------------------
|
|
65
|
+
# Tenant guard (call first on every response)
|
|
66
|
+
# ------------------------------------------------------------------
|
|
67
|
+
|
|
68
|
+
def check_tenant(self, resource_tenant_id: str) -> bool:
|
|
69
|
+
"""Ensure *resource_tenant_id* matches the authenticated user's tenant."""
|
|
70
|
+
return resource_tenant_id == self.tenant_id
|
|
71
|
+
|
|
72
|
+
# ------------------------------------------------------------------
|
|
73
|
+
# Resource-specific filters
|
|
74
|
+
# ------------------------------------------------------------------
|
|
75
|
+
|
|
76
|
+
def filter_portfolios(self, portfolios: List[dict]) -> List[dict]:
|
|
77
|
+
"""Return only portfolios the user is authorised to see."""
|
|
78
|
+
if self._is_global:
|
|
79
|
+
return portfolios
|
|
80
|
+
return self.apply_filter(portfolios, "portfolio_id")
|
|
81
|
+
|
|
82
|
+
def filter_schemes(self, schemes: List[dict]) -> List[dict]:
|
|
83
|
+
"""Return only MF schemes the user is authorised to see."""
|
|
84
|
+
if self._is_global:
|
|
85
|
+
return schemes
|
|
86
|
+
return self.apply_filter(schemes, "scheme_id")
|
|
87
|
+
|
|
88
|
+
def filter_trades(self, trades: List[dict]) -> List[dict]:
|
|
89
|
+
"""Return only trades belonging to the user's authorised portfolios."""
|
|
90
|
+
if self._is_global:
|
|
91
|
+
return trades
|
|
92
|
+
return self.apply_filter(trades, "portfolio_id")
|
|
93
|
+
|
|
94
|
+
def filter_clients(self, clients: List[dict]) -> List[dict]:
|
|
95
|
+
"""Return only clients the user is authorised to see.
|
|
96
|
+
|
|
97
|
+
Operations and admin/compliance roles can see all clients.
|
|
98
|
+
Other roles are filtered by client_id in scoped_ids.
|
|
99
|
+
"""
|
|
100
|
+
if self.role in _CLIENT_GLOBAL_ROLES:
|
|
101
|
+
return clients
|
|
102
|
+
return self.apply_filter(clients, "client_id")
|
|
103
|
+
|
|
104
|
+
def filter_folios(self, folios: List[dict]) -> List[dict]:
|
|
105
|
+
"""Return only MF folios for schemes the user can access."""
|
|
106
|
+
if self._is_global:
|
|
107
|
+
return folios
|
|
108
|
+
return self.apply_filter(folios, "scheme_id")
|
|
109
|
+
|
|
110
|
+
def filter_invoices(self, invoices: List[dict]) -> List[dict]:
|
|
111
|
+
"""Return invoices matching the user's portfolio_id or client_id scope."""
|
|
112
|
+
if self._is_global:
|
|
113
|
+
return invoices
|
|
114
|
+
return [
|
|
115
|
+
inv for inv in invoices
|
|
116
|
+
if inv.get("portfolio_id") in self._scoped_set
|
|
117
|
+
or inv.get("client_id") in self._scoped_set
|
|
118
|
+
]
|
|
119
|
+
|
|
120
|
+
def filter_settlements(self, settlements: List[dict]) -> List[dict]:
|
|
121
|
+
"""Return settlements whose trade's portfolio is in scope."""
|
|
122
|
+
if self._is_global:
|
|
123
|
+
return settlements
|
|
124
|
+
return self.apply_filter(settlements, "portfolio_id")
|
|
125
|
+
|
|
126
|
+
def filter_notifications(self, notifications: List[dict]) -> List[dict]:
|
|
127
|
+
"""Return only notifications addressed to the current user.
|
|
128
|
+
|
|
129
|
+
Admins can see all notifications.
|
|
130
|
+
"""
|
|
131
|
+
if self.role == "admin":
|
|
132
|
+
return notifications
|
|
133
|
+
return [n for n in notifications if n.get("recipient_id") == self.user_id]
|
|
134
|
+
|
|
135
|
+
# ------------------------------------------------------------------
|
|
136
|
+
# Single-resource access check
|
|
137
|
+
# ------------------------------------------------------------------
|
|
138
|
+
|
|
139
|
+
def can_access(self, resource_type: str, resource_id: str) -> bool:
|
|
140
|
+
"""Check whether the user can access a specific resource.
|
|
141
|
+
|
|
142
|
+
Global-access roles always return True.
|
|
143
|
+
"""
|
|
144
|
+
if self._is_global:
|
|
145
|
+
return True
|
|
146
|
+
return resource_id in self._scoped_set
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
# ======================================================================
|
|
150
|
+
# FastAPI dependency
|
|
151
|
+
# ======================================================================
|
|
152
|
+
|
|
153
|
+
def get_rls_filter(request=None, user=None) -> RLSFilter:
|
|
154
|
+
"""Build an RLSFilter from a JWTIdentity.
|
|
155
|
+
|
|
156
|
+
Typical usage as a FastAPI dependency::
|
|
157
|
+
|
|
158
|
+
from contextsynapse.context.acl import JWTIdentity
|
|
159
|
+
from contextsynapse.security.rls import get_rls_filter, RLSFilter
|
|
160
|
+
|
|
161
|
+
@router.get("/portfolios")
|
|
162
|
+
async def list_portfolios(
|
|
163
|
+
rls: RLSFilter = Depends(get_rls_filter),
|
|
164
|
+
):
|
|
165
|
+
all_portfolios = fetch_all(...)
|
|
166
|
+
return rls.filter_portfolios(all_portfolios)
|
|
167
|
+
|
|
168
|
+
To wire this up, register a concrete dependency in your app that
|
|
169
|
+
resolves the JWTIdentity first::
|
|
170
|
+
|
|
171
|
+
from contextsynapse.security.jwt_identity import extract_identity_from_token
|
|
172
|
+
|
|
173
|
+
async def _resolve_identity(request: Request) -> JWTIdentity:
|
|
174
|
+
token = request.headers.get("Authorization", "").removeprefix("Bearer ")
|
|
175
|
+
identity = extract_identity_from_token(token)
|
|
176
|
+
if identity is None:
|
|
177
|
+
raise HTTPException(401, "Invalid token")
|
|
178
|
+
return identity
|
|
179
|
+
|
|
180
|
+
def get_rls(identity: JWTIdentity = Depends(_resolve_identity)) -> RLSFilter:
|
|
181
|
+
scoped = identity.portfolio_ids or identity.client_ids or []
|
|
182
|
+
return RLSFilter(
|
|
183
|
+
user_id=identity.sub,
|
|
184
|
+
role=identity.role.value,
|
|
185
|
+
scoped_ids=scoped,
|
|
186
|
+
tenant_id=identity.tenant,
|
|
187
|
+
)
|
|
188
|
+
"""
|
|
189
|
+
# This function serves as a template. Callers should override it
|
|
190
|
+
# with a concrete Depends() chain that extracts JWTIdentity from
|
|
191
|
+
# the request. The default implementation is a no-op for import
|
|
192
|
+
# compatibility.
|
|
193
|
+
if user is not None:
|
|
194
|
+
# user is expected to be a JWTIdentity-like object
|
|
195
|
+
scoped = getattr(user, "portfolio_ids", None) or getattr(user, "scheme_ids", None) or []
|
|
196
|
+
return RLSFilter(
|
|
197
|
+
user_id=getattr(user, "sub", ""),
|
|
198
|
+
role=getattr(user, "role", "").value if hasattr(getattr(user, "role", ""), "value") else str(getattr(user, "role", "")),
|
|
199
|
+
scoped_ids=list(scoped),
|
|
200
|
+
tenant_id=getattr(user, "tenant", ""),
|
|
201
|
+
)
|
|
202
|
+
raise ValueError(
|
|
203
|
+
"get_rls_filter requires a JWTIdentity user. "
|
|
204
|
+
"Wire it as a FastAPI Depends() with your auth dependency."
|
|
205
|
+
)
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AIQL Input Sanitization
|
|
3
|
+
========================
|
|
4
|
+
Prevents SQL/AIQL injection by sanitizing user-provided identifiers and values
|
|
5
|
+
before they are interpolated into AIQL query strings.
|
|
6
|
+
|
|
7
|
+
Usage::
|
|
8
|
+
|
|
9
|
+
from contextsynapse.security.sanitize import sanitize_aiql_identifier, sanitize_aiql_value
|
|
10
|
+
|
|
11
|
+
safe_label = sanitize_aiql_identifier(user_label)
|
|
12
|
+
safe_value = sanitize_aiql_value(user_value)
|
|
13
|
+
query = f'SELECT * FROM {safe_label} WHERE name = "{safe_value}"'
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import re
|
|
19
|
+
from typing import Any
|
|
20
|
+
|
|
21
|
+
# AIQL keywords that must never appear in user-provided identifiers
|
|
22
|
+
_AIQL_KEYWORDS = frozenset({
|
|
23
|
+
"select", "from", "where", "create", "delete", "update", "drop",
|
|
24
|
+
"insert", "node", "edge", "set", "match", "find", "show", "use",
|
|
25
|
+
"graph", "explain", "load", "extract", "chunk", "embed", "index",
|
|
26
|
+
"grant", "revoke", "union", "and", "or", "not", "in", "like",
|
|
27
|
+
"between", "exists", "null", "true", "false",
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
# Pattern: only alphanumeric, underscore, hyphen allowed in identifiers
|
|
31
|
+
_SAFE_IDENTIFIER = re.compile(r"^[a-zA-Z_][a-zA-Z0-9_-]*$")
|
|
32
|
+
|
|
33
|
+
# Pattern: namespace names — no colons, wildcards, or special Redis chars
|
|
34
|
+
_SAFE_NAMESPACE = re.compile(r"^[a-zA-Z0-9][a-zA-Z0-9_-]*$")
|
|
35
|
+
|
|
36
|
+
# Pattern for each segment within a hierarchical path
|
|
37
|
+
_SAFE_SEGMENT = re.compile(r"^[a-zA-Z0-9][a-zA-Z0-9_-]*$")
|
|
38
|
+
|
|
39
|
+
# Reserved namespace names that cannot be created by users
|
|
40
|
+
_RESERVED_NAMESPACES = frozenset({
|
|
41
|
+
"lru", "names", "deleted", "sync",
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
# System-internal namespaces — bypass validation entirely
|
|
45
|
+
_SYSTEM_NAMESPACES = frozenset({
|
|
46
|
+
"default", "__intelligence_config__",
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
# Valid top-level scopes for hierarchical context paths
|
|
50
|
+
VALID_SCOPES = frozenset({"market", "portfolio", "client", "frozen"})
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def parse_context_path(path: str) -> tuple:
|
|
54
|
+
"""Parse a context path into (scope, segments).
|
|
55
|
+
|
|
56
|
+
Flat paths (no colon) default to 'market' scope.
|
|
57
|
+
Hierarchical paths split on ':' — first segment is the scope.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
path: Context path like 'tcs' or 'market:tcs' or 'portfolio:p001:holdings'
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
Tuple of (scope, [remaining_segments])
|
|
64
|
+
"""
|
|
65
|
+
if ":" not in path:
|
|
66
|
+
return ("market", [path])
|
|
67
|
+
|
|
68
|
+
parts = path.split(":")
|
|
69
|
+
scope = parts[0]
|
|
70
|
+
segments = parts[1:]
|
|
71
|
+
return (scope, segments)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def validate_namespace(name: str) -> str:
|
|
75
|
+
"""Validate a context namespace name for safe use in Redis keys.
|
|
76
|
+
|
|
77
|
+
Accepts both flat names ('tcs') and hierarchical paths ('market:tcs',
|
|
78
|
+
'portfolio:p001:holdings'). Flat names are validated as single segments.
|
|
79
|
+
Hierarchical paths are validated segment-by-segment with scope checking.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
name: Namespace name or hierarchical path to validate.
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
The validated name (unchanged).
|
|
86
|
+
|
|
87
|
+
Raises:
|
|
88
|
+
ValueError: If the name is invalid, reserved, or too long.
|
|
89
|
+
"""
|
|
90
|
+
if not name or not isinstance(name, str):
|
|
91
|
+
raise ValueError("Namespace must be a non-empty string")
|
|
92
|
+
|
|
93
|
+
name = name.strip()
|
|
94
|
+
|
|
95
|
+
# System-internal namespaces bypass all validation
|
|
96
|
+
if name in _SYSTEM_NAMESPACES:
|
|
97
|
+
return name
|
|
98
|
+
|
|
99
|
+
if ":" in name:
|
|
100
|
+
# Hierarchical path: scope:segment[:segment...]
|
|
101
|
+
parts = name.split(":")
|
|
102
|
+
scope = parts[0]
|
|
103
|
+
segments = parts[1:]
|
|
104
|
+
|
|
105
|
+
if scope not in VALID_SCOPES:
|
|
106
|
+
raise ValueError(
|
|
107
|
+
f"Invalid scope: {scope!r} — must be one of {sorted(VALID_SCOPES)}"
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
if len(segments) > 3:
|
|
111
|
+
raise ValueError(
|
|
112
|
+
f"Namespace path too deep: {len(segments) + 1} segments (max 4)"
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
for seg in segments:
|
|
116
|
+
if not seg:
|
|
117
|
+
raise ValueError("Empty segment in namespace path")
|
|
118
|
+
if not _SAFE_SEGMENT.match(seg):
|
|
119
|
+
raise ValueError(
|
|
120
|
+
f"Invalid segment: {seg!r} — only letters, digits, underscores, "
|
|
121
|
+
f"and hyphens allowed"
|
|
122
|
+
)
|
|
123
|
+
if len(seg) > 128:
|
|
124
|
+
raise ValueError(f"Segment too long: {len(seg)} chars (max 128)")
|
|
125
|
+
|
|
126
|
+
return name
|
|
127
|
+
else:
|
|
128
|
+
# Flat name (backward compat)
|
|
129
|
+
if not _SAFE_NAMESPACE.match(name):
|
|
130
|
+
raise ValueError(
|
|
131
|
+
f"Invalid namespace: {name!r} — only letters, digits, underscores, "
|
|
132
|
+
f"and hyphens allowed (no colons, wildcards, or spaces)"
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
if len(name) > 128:
|
|
136
|
+
raise ValueError(f"Namespace too long: {len(name)} chars (max 128)")
|
|
137
|
+
|
|
138
|
+
if name.lower() in _RESERVED_NAMESPACES:
|
|
139
|
+
raise ValueError(f"Reserved namespace name: {name!r}")
|
|
140
|
+
|
|
141
|
+
return name
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def sanitize_aiql_identifier(name: str) -> str:
|
|
145
|
+
"""Sanitize a label or property name for use in AIQL queries.
|
|
146
|
+
|
|
147
|
+
Allows only alphanumeric characters, underscores, and hyphens.
|
|
148
|
+
Rejects AIQL keywords to prevent query manipulation.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
name: User-provided label or property name.
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
Sanitized identifier string.
|
|
155
|
+
|
|
156
|
+
Raises:
|
|
157
|
+
ValueError: If the identifier is invalid or contains AIQL keywords.
|
|
158
|
+
"""
|
|
159
|
+
if not name or not isinstance(name, str):
|
|
160
|
+
raise ValueError("Identifier must be a non-empty string")
|
|
161
|
+
|
|
162
|
+
name = name.strip()
|
|
163
|
+
|
|
164
|
+
if not _SAFE_IDENTIFIER.match(name):
|
|
165
|
+
raise ValueError(
|
|
166
|
+
f"Invalid identifier: {name!r} — only letters, digits, underscores, and hyphens allowed"
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
if name.lower() in _AIQL_KEYWORDS:
|
|
170
|
+
raise ValueError(f"Reserved AIQL keyword: {name!r}")
|
|
171
|
+
|
|
172
|
+
if len(name) > 128:
|
|
173
|
+
raise ValueError(f"Identifier too long: {len(name)} chars (max 128)")
|
|
174
|
+
|
|
175
|
+
return name
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def sanitize_aiql_value(value: Any) -> str:
|
|
179
|
+
"""Sanitize a value for use in AIQL string literals.
|
|
180
|
+
|
|
181
|
+
Escapes double quotes, backslashes, and strips dangerous characters
|
|
182
|
+
that could break out of a quoted string context.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
value: User-provided value (will be converted to string).
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
Escaped string safe for use inside double-quoted AIQL literals.
|
|
189
|
+
"""
|
|
190
|
+
if value is None:
|
|
191
|
+
return ""
|
|
192
|
+
|
|
193
|
+
s = str(value)
|
|
194
|
+
|
|
195
|
+
# Strip null bytes
|
|
196
|
+
s = s.replace("\x00", "")
|
|
197
|
+
|
|
198
|
+
# Escape backslashes first, then double quotes
|
|
199
|
+
s = s.replace("\\", "\\\\")
|
|
200
|
+
s = s.replace('"', '\\"')
|
|
201
|
+
|
|
202
|
+
# Strip semicolons — prevent statement chaining
|
|
203
|
+
s = s.replace(";", "")
|
|
204
|
+
|
|
205
|
+
# Limit length to prevent abuse
|
|
206
|
+
if len(s) > 4096:
|
|
207
|
+
s = s[:4096]
|
|
208
|
+
|
|
209
|
+
return s
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def build_safe_where_clause(where_dict: dict) -> str:
|
|
213
|
+
"""Build a safe AIQL WHERE clause from a user-provided dict.
|
|
214
|
+
|
|
215
|
+
Each key is sanitized as an identifier, each value as a string literal.
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
where_dict: Dict of {property_name: value} filters.
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
AIQL WHERE clause string, e.g. ``'WHERE name = "Alice" AND age = "30"'``
|
|
222
|
+
Returns empty string if where_dict is empty or None.
|
|
223
|
+
"""
|
|
224
|
+
if not where_dict:
|
|
225
|
+
return ""
|
|
226
|
+
|
|
227
|
+
conditions = []
|
|
228
|
+
for k, v in where_dict.items():
|
|
229
|
+
safe_key = sanitize_aiql_identifier(k)
|
|
230
|
+
safe_val = sanitize_aiql_value(v)
|
|
231
|
+
conditions.append(f'{safe_key} = "{safe_val}"')
|
|
232
|
+
|
|
233
|
+
return " WHERE " + " AND ".join(conditions)
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""Scoped Encryption — transparent per-scope encryption for context data.
|
|
2
|
+
|
|
3
|
+
Market data is plaintext (public). Portfolio, client, and frozen data are
|
|
4
|
+
encrypted with Fernet (AES-128-CBC). Client data uses per-client derived keys.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import base64
|
|
9
|
+
import hashlib
|
|
10
|
+
import json
|
|
11
|
+
import logging
|
|
12
|
+
import os
|
|
13
|
+
from typing import Any, Dict
|
|
14
|
+
|
|
15
|
+
from contextsynapse.security.sanitize import parse_context_path
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
# Scopes that require encryption
|
|
20
|
+
_ENCRYPTED_SCOPES = {"portfolio", "client", "frozen"}
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
from cryptography.fernet import Fernet
|
|
24
|
+
_FERNET_AVAILABLE = True
|
|
25
|
+
except ImportError:
|
|
26
|
+
_FERNET_AVAILABLE = False
|
|
27
|
+
logger.warning("cryptography package not installed — scoped encryption disabled")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def derive_client_key(tenant_key: bytes, client_id: str) -> bytes:
|
|
31
|
+
"""Derive a per-client Fernet key from the tenant master key.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
tenant_key: Raw bytes of the tenant master key.
|
|
35
|
+
client_id: Unique client identifier used as PBKDF2 salt.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
URL-safe base64-encoded 32-byte key suitable for Fernet.
|
|
39
|
+
"""
|
|
40
|
+
derived = hashlib.pbkdf2_hmac("sha256", tenant_key, client_id.encode(), 100_000)
|
|
41
|
+
return base64.urlsafe_b64encode(derived[:32])
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class ScopedEncryptor:
|
|
45
|
+
"""Transparent encryption based on context scope.
|
|
46
|
+
|
|
47
|
+
- ``market`` scope: plaintext JSON (no encryption — public data)
|
|
48
|
+
- ``portfolio`` scope: Fernet-encrypted with tenant key
|
|
49
|
+
- ``client`` scope: Fernet-encrypted with per-client derived key
|
|
50
|
+
- ``frozen`` scope: Fernet-encrypted with tenant key
|
|
51
|
+
|
|
52
|
+
Usage::
|
|
53
|
+
|
|
54
|
+
enc = ScopedEncryptor()
|
|
55
|
+
raw = enc.encrypt("portfolio:p001:holdings", {"holdings": [...]})
|
|
56
|
+
data = enc.decrypt("portfolio:p001:holdings", raw)
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
def __init__(self, tenant_key: str = None):
|
|
60
|
+
self._tenant_key = (
|
|
61
|
+
tenant_key or os.environ.get("CONTEXTSYNAPSE_TENANT_KEY") or os.environ.get("AICONTEXTDB_TENANT_KEY", "")
|
|
62
|
+
).encode()
|
|
63
|
+
|
|
64
|
+
if self._tenant_key and _FERNET_AVAILABLE:
|
|
65
|
+
# Derive a Fernet-compatible 32-byte key from the tenant key string
|
|
66
|
+
dk = hashlib.pbkdf2_hmac(
|
|
67
|
+
"sha256", self._tenant_key, b"contextcore_scope", 100_000
|
|
68
|
+
)
|
|
69
|
+
self._tenant_fernet_key = base64.urlsafe_b64encode(dk[:32])
|
|
70
|
+
else:
|
|
71
|
+
self._tenant_fernet_key = None
|
|
72
|
+
|
|
73
|
+
def _fernet_for(self, context_path: str) -> "Fernet":
|
|
74
|
+
"""Return the correct Fernet instance for the given context path.
|
|
75
|
+
|
|
76
|
+
Client paths use a per-client derived key; all other encrypted scopes
|
|
77
|
+
use the shared tenant key.
|
|
78
|
+
"""
|
|
79
|
+
scope, parts = parse_context_path(context_path)
|
|
80
|
+
|
|
81
|
+
if scope == "client" and parts:
|
|
82
|
+
client_id = parts[0]
|
|
83
|
+
key = derive_client_key(self._tenant_key, client_id)
|
|
84
|
+
else:
|
|
85
|
+
key = self._tenant_fernet_key
|
|
86
|
+
|
|
87
|
+
return Fernet(key)
|
|
88
|
+
|
|
89
|
+
def encrypt(self, context_path: str, data: Dict[str, Any]) -> bytes:
|
|
90
|
+
"""Encrypt context data based on scope.
|
|
91
|
+
|
|
92
|
+
Market data is returned as plain JSON bytes. All other recognised
|
|
93
|
+
scopes (portfolio, client, frozen) are Fernet-encrypted.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
context_path: Scoped path like ``"portfolio:p001:holdings"``.
|
|
97
|
+
data: JSON-serialisable dict to store.
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
Encrypted bytes (Fernet token) or plain JSON bytes for market data.
|
|
101
|
+
"""
|
|
102
|
+
scope, _ = parse_context_path(context_path)
|
|
103
|
+
serialized = json.dumps(data, default=str).encode("utf-8")
|
|
104
|
+
|
|
105
|
+
if scope not in _ENCRYPTED_SCOPES or not _FERNET_AVAILABLE:
|
|
106
|
+
return serialized
|
|
107
|
+
|
|
108
|
+
f = self._fernet_for(context_path)
|
|
109
|
+
return f.encrypt(serialized)
|
|
110
|
+
|
|
111
|
+
def decrypt(self, context_path: str, raw: bytes) -> Dict[str, Any]:
|
|
112
|
+
"""Decrypt context data based on scope.
|
|
113
|
+
|
|
114
|
+
Market data is decoded directly from JSON. Encrypted scopes are
|
|
115
|
+
decrypted with the appropriate Fernet key before JSON parsing.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
context_path: Scoped path matching the one used during encryption.
|
|
119
|
+
raw: Bytes returned by :meth:`encrypt`.
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
Original data dict.
|
|
123
|
+
"""
|
|
124
|
+
scope, _ = parse_context_path(context_path)
|
|
125
|
+
|
|
126
|
+
if scope not in _ENCRYPTED_SCOPES or not _FERNET_AVAILABLE:
|
|
127
|
+
return json.loads(raw)
|
|
128
|
+
|
|
129
|
+
f = self._fernet_for(context_path)
|
|
130
|
+
decrypted = f.decrypt(raw)
|
|
131
|
+
return json.loads(decrypted)
|