parse-stack-next 5.5.4 → 5.5.5

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.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -6
  3. data/README.md +26 -13
  4. data/bin/parse-console +9 -1
  5. data/docs/TEST_SERVER.md +115 -238
  6. data/docs/mcp_guide.md +1 -1
  7. data/docs/mongodb_index_optimization_guide.md +3 -2
  8. data/docs/usage_guide.md +1 -1
  9. data/docs/yard-template/default/fulldoc/html/css/common.css +52 -9
  10. data/docs/yard-template/default/fulldoc/html/css/full_list.css +40 -13
  11. data/lib/parse/agent/constraint_translator.rb +18 -18
  12. data/lib/parse/agent/errors.rb +29 -7
  13. data/lib/parse/agent/metadata_dsl.rb +6 -6
  14. data/lib/parse/agent/tools.rb +250 -59
  15. data/lib/parse/agent.rb +42 -30
  16. data/lib/parse/api/aggregate.rb +3 -3
  17. data/lib/parse/api/cloud_functions.rb +19 -10
  18. data/lib/parse/api/objects.rb +8 -8
  19. data/lib/parse/api/users.rb +9 -9
  20. data/lib/parse/atlas_search/session.rb +34 -34
  21. data/lib/parse/atlas_search.rb +243 -110
  22. data/lib/parse/client/body_builder.rb +10 -10
  23. data/lib/parse/client/logging.rb +5 -2
  24. data/lib/parse/client/profiling.rb +5 -2
  25. data/lib/parse/client/protocol.rb +1 -1
  26. data/lib/parse/client/url_redaction.rb +94 -0
  27. data/lib/parse/client.rb +43 -28
  28. data/lib/parse/embeddings/image_fetch.rb +6 -1
  29. data/lib/parse/embeddings/voyage.rb +16 -17
  30. data/lib/parse/live_query/client.rb +7 -7
  31. data/lib/parse/live_query/subscription.rb +1 -1
  32. data/lib/parse/lock.rb +1 -1
  33. data/lib/parse/lock_backend.rb +118 -2
  34. data/lib/parse/model/acl.rb +24 -24
  35. data/lib/parse/model/classes/job_schedule.rb +8 -8
  36. data/lib/parse/model/classes/job_status.rb +9 -9
  37. data/lib/parse/model/classes/role.rb +49 -49
  38. data/lib/parse/model/classes/session.rb +2 -2
  39. data/lib/parse/model/classes/user.rb +66 -66
  40. data/lib/parse/model/core/builder.rb +7 -7
  41. data/lib/parse/model/core/create_lock.rb +1 -1
  42. data/lib/parse/model/core/properties.rb +4 -4
  43. data/lib/parse/model/file.rb +57 -16
  44. data/lib/parse/model/model.rb +19 -19
  45. data/lib/parse/model/object.rb +38 -38
  46. data/lib/parse/model/pointer.rb +4 -4
  47. data/lib/parse/model/push.rb +5 -5
  48. data/lib/parse/mongodb.rb +84 -26
  49. data/lib/parse/pipeline_security.rb +2 -2
  50. data/lib/parse/query/constraints.rb +38 -38
  51. data/lib/parse/query.rb +151 -75
  52. data/lib/parse/retrieval/reranker/cohere.rb +30 -0
  53. data/lib/parse/schema.rb +1 -1
  54. data/lib/parse/stack/version.rb +1 -1
  55. data/lib/parse/stack.rb +23 -10
  56. data/lib/parse/two_factor_auth/user_extension.rb +25 -25
  57. data/lib/parse/webhooks/payload.rb +35 -35
  58. data/lib/parse/webhooks/registration.rb +2 -2
  59. data/lib/parse/webhooks/replay_protection.rb +16 -16
  60. data/lib/parse/webhooks.rb +11 -11
  61. data/parse-stack-next.gemspec +19 -1
  62. metadata +2 -38
  63. data/.bundle/config +0 -5
  64. data/.env.sample +0 -138
  65. data/.env.test +0 -10
  66. data/.github/ISSUE_TEMPLATE/bug_report.yml +0 -105
  67. data/.github/ISSUE_TEMPLATE/feature_request.yml +0 -67
  68. data/.github/dependabot.yml +0 -13
  69. data/.github/workflows/codeql.yml +0 -44
  70. data/.github/workflows/docs.yml +0 -39
  71. data/.github/workflows/release.yml +0 -43
  72. data/.github/workflows/ruby.yml +0 -38
  73. data/.gitignore +0 -56
  74. data/.ruby-version +0 -1
  75. data/.solargraph.yml +0 -22
  76. data/.vscode/settings.json +0 -3
  77. data/.yardopts +0 -19
  78. data/Gemfile +0 -43
  79. data/Gemfile.lock +0 -198
  80. data/Makefile +0 -63
  81. data/Rakefile +0 -825
  82. data/config/parse-config.json +0 -12
  83. data/scripts/debug-ips.js +0 -35
  84. data/scripts/docker/Dockerfile.parse +0 -17
  85. data/scripts/docker/atlas-init.js +0 -284
  86. data/scripts/docker/docker-compose.atlas.yml +0 -80
  87. data/scripts/docker/docker-compose.test.yml +0 -159
  88. data/scripts/docker/docker-compose.verifyemail.yml +0 -4
  89. data/scripts/docker/mongo-init.js +0 -21
  90. data/scripts/docker/preflight.sh +0 -76
  91. data/scripts/eval_mcp_with_lm_studio.rb +0 -274
  92. data/scripts/start-parse.sh +0 -154
  93. data/scripts/start_mcp_server.rb +0 -78
  94. data/scripts/test_server_connection.rb +0 -82
  95. data/scripts/vector_prototype/create_vector_index.js +0 -105
  96. data/scripts/vector_prototype/fetch_embeddings.py +0 -241
  97. data/scripts/vector_prototype/fixture_manifest.json +0 -9
  98. data/scripts/vector_prototype/query_prototype.rb +0 -84
  99. data/scripts/vector_prototype/run.sh +0 -34
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 97d41b5209b0365349812d166993421d4f25ae734dff25141c141decf0c54cc4
4
- data.tar.gz: c0e3b483da29241890500469939f79a44396966e321827283dc3be327d7bdc6c
3
+ metadata.gz: ef7d7de047cc0a3b4a21f3b080745f36f67cd2a53a81787fe53fdbb68fc3ec41
4
+ data.tar.gz: 74af45b9e2f334ca13c99419fe71d1653b9a218dffcf2a02ff801088ab86850b
5
5
  SHA512:
6
- metadata.gz: e01b065bc9e57307d95dcba6ae474a025130d694f5cf30560441af37d31f047a79fd1c65dc3e8543024a1d449a31b30bf82d7a3ba4908a512b61975de4558353
7
- data.tar.gz: d5b0a9ac73f321dc78953924d175549bf6d5ee96918907851b2dc1589dd3131941d2c1f5eb30a166b9b806abea70d416ae7f869eb9dfcf781b3f10f1879f629b
6
+ metadata.gz: e31f81f269bf874438617b00a01f1fb095fc7853f544d0a38f02aff8f58e21b14e8561650e3d3fb29bad6ff6276722992451a4f78b640481de3e5ab421cd033a
7
+ data.tar.gz: 71063ac383cb393af56969a1b631885585f5863b764218d7bee65b79a34cc9b469a885e96cf5bac5239ec66c140602e2bbe8197a46fa90ff4568cb240571e80f
data/CHANGELOG.md CHANGED
@@ -4,8 +4,10 @@
4
4
 
5
5
  #### Dependency updates
6
6
 
7
- - **CHANGED**: Bumped `graphql` to 2.6.5 (from 2.6.3) in the locked
8
- dependencies.
7
+ - **CHANGED**: Bumped locked dependencies to their latest compatible
8
+ releases — `graphql` 2.6.3 → 2.6.5, `i18n` 1.15.1 → 1.15.2, `json` 2.19.9
9
+ → 2.21.1, `pp` 0.6.3 → 0.6.4, `rdoc` 7.2.0 → 8.0.0, `tilt` 2.7.0 → 2.8.0,
10
+ and `yard` 0.9.44 → 0.9.45.
9
11
 
10
12
  ### 5.5.3
11
13
 
@@ -4506,17 +4508,17 @@ query.where(genre: "rock").last_updated(limit: 3)
4506
4508
 
4507
4509
  ```ruby
4508
4510
  # Default behavior - pointers for storage (backward compatible)
4509
- post.assets.as_json
4511
+ post.documents.as_json
4510
4512
  # => [{"__type"=>"Pointer", "className"=>"Document", "objectId"=>"abc"}, ...]
4511
4513
 
4512
4514
  # Serialize with fetched fields (no autofetch, pointers stay as pointers)
4513
- post.assets.as_json(pointers_only: false)
4515
+ post.documents.as_json(pointers_only: false)
4514
4516
  # => [{"objectId"=>"abc", "file"=>{...}, "caption"=>"My photo", ...}, ...]
4515
4517
 
4516
- # In webhooks, manually override assets serialization:
4518
+ # In webhooks, manually override documents serialization:
4517
4519
  cloud_results.map do |post|
4518
4520
  json = post.as_json
4519
- json['assets'] = post.assets.as_json(pointers_only: false) if post.assets.any?
4521
+ json['documents'] = post.documents.as_json(pointers_only: false) if post.documents.any?
4520
4522
  json
4521
4523
  end
4522
4524
  ```
data/README.md CHANGED
@@ -4,14 +4,14 @@
4
4
 
5
5
  A full-featured Ruby client SDK for [Parse Server](http://parseplatform.org/). [parse-stack-next](https://github.com/neurosynq/parse-stack-next) is a Ruby client SDK, REST client, and Active Model ORM for [Parse Server](http://parseplatform.org/), combining a low-level API client, a query engine, an object-relational mapper (ORM), and a Cloud Code Webhooks rack application in a single gem.
6
6
 
7
- ### What's new in 5.5
7
+ ## What's new in 5.5
8
8
 
9
9
  - **5.5.0 — Multimodal bytes-fetch with magic-byte MIME verification** — `embed_image ..., source: :bytes` has the SDK download an image itself through the `Parse::File.safe_open_url` SSRF primitive, verify the content by **magic-byte sniff** (the `Content-Type` header is never consulted — a `.jpg` URL serving HTML is refused), cross-check the URL extension, enforce a `Parse::Embeddings.allowed_image_types` allowlist, strip EXIF/XMP metadata **by default** (JPEG APP1, PNG `eXIf`, WebP `EXIF`/`XMP ` chunks; opt out with `exif_strip: false`), and forward the verified bytes to Voyage/Cohere as a base64 data URI. No provider-side URL fetch occurs, so the `trust_provider_url_fetch` sentinel is not required — the host allowlist still applies. See [CHANGELOG.md](./CHANGELOG.md)
10
10
  - **5.5.0 — Embedding-model migration tooling** — `Class.reembed!(only_stale: true)` bulk re-embeds rows through the current provider/model (resumable; skips rows already current), driven by the new auto-declared `<into>_meta` provenance sibling (`{provider, model, dimensions, modality, embedded_at}`, stamped on every recompute). `Parse::Embeddings::BatchEmbedder` adds batch-level requests-per-minute pacing and exponential backoff for bulk jobs; `Parse::Embeddings::Cache.enable!` adds an opt-in query-embed cache keyed by `(provider, model, input_type, input-hash)` so repeated identical queries skip the provider round-trip. See [CHANGELOG.md](./CHANGELOG.md)
11
11
  - **5.5.0 — Vector index drift detection** — on first auto-discovered use of an Atlas vectorSearch index, the SDK verifies the deployed index's `numDimensions`/`similarity` against the `:vector` property declaration and confirms a registered `agent_tenant_scope` field is covered as a `type: "filter"` path. Policy via `Parse::VectorSearch.index_drift_policy` (`:warn` default / `:raise` / `:ignore`). `Parse::Schema::SearchIndexMigrator` now auto-includes the tenant-scope field in `vectorSearch` declarations, so newly created indexes support tenant-scoped pre-filtering out of the box. See [CHANGELOG.md](./CHANGELOG.md)
12
12
  - **5.5.0 — Retrieval spend-cap and filter hardening** — the per-tenant embedding spend cap now covers every query-embed path (`find_similar(text:)`, `hybrid_search(text:)`, `Parse::Retrieval.retrieve`), not just the `semantic_search` agent tool; tenant identity resolves through the ambient `Parse.with_cache_tenant` scope. Caller-supplied retrieval filters now translate Parse pointer values to storage form (`{ owner: user }` → `{ "_p_owner" => "_User$id" }`), so pointer filters match rows instead of silently matching nothing. See [CHANGELOG.md](./CHANGELOG.md)
13
13
 
14
- ### What's new in 5.4
14
+ ## What's new in 5.4
15
15
 
16
16
  - **5.4.0 — Hybrid search + reranking for RAG** — `Class.hybrid_search(text:, lexical:, vector:, k:, fusion:)` fuses a lexical Atlas Search branch with a `$vectorSearch` branch using reciprocal-rank fusion (RRF): lexical search nails exact tokens (codes, proper nouns), vector search nails paraphrase, and fusing the two beats either alone. Each branch enforces ACL/CLP independently before fusion (no separate hydration fetch to secure); results carry `#hybrid_score` / `#hybrid_ranks`. `Parse::VectorSearch::Hybrid.rank_fusion_supported?` detects Atlas 8.0+ native `$rankFusion` by a cached behavioural probe (native execution is opt-in; client-side RRF is the always-enforced default). `Parse::Retrieval::Reranker` adds cross-encoder reranking (`Reranker::Cohere` over `/v2/rerank`, plus a deterministic `Reranker::Fixture`), wired into `Parse::Retrieval.retrieve(hybrid:, rerank:)`. `Parse::Embeddings::SpendCap` adds an opt-in per-tenant embedding token cap (hard-refuse) at the `semantic_search` agent-tool boundary. See [CHANGELOG.md](./CHANGELOG.md) and [`docs/atlas_vector_search_guide.md`](./docs/atlas_vector_search_guide.md)
17
17
  - **5.4.0 — Vector backfill, visibility, and webhook redaction** — `Class.embed_pending!` backfills embeddings for records whose managed `:vector` field is null (objectId-cursor pagination); `Parse::Object#compute_embedding!` forces an in-place recompute without a save; `vector_visibility :owner_only | :public` controls whether a class's vectors appear in `as_json` by default; and webhook trigger payloads now strip declared `:vector` columns by default (a `:public` class keeps them). See [CHANGELOG.md](./CHANGELOG.md)
@@ -20,13 +20,13 @@ A full-featured Ruby client SDK for [Parse Server](http://parseplatform.org/). [
20
20
  - **5.4.0 — Audience hash queries persist correctly** — `Parse::Audience#query` is now stored as a JSON string on the wire to match Parse Server's `_Audience.query` column type, so saving an audience with a `Hash` query no longer fails the server schema check. The public API is unchanged — assign a `Hash`, read a `Hash` back. See [CHANGELOG.md](./CHANGELOG.md)
21
21
  - **5.4.0 — Faster AtlasSearch role-cache expiry** — `Parse::AtlasSearch` `role_cache_ttl` now defaults to 30 seconds (was 120) so a role grant or revoke is reflected in `$search` ACL decisions sooner, at the cost of slightly more frequent role lookups. See [CHANGELOG.md](./CHANGELOG.md)
22
22
 
23
- ### What's new in 5.3
23
+ ## What's new in 5.3
24
24
 
25
25
  - **5.3.0 — Run webhook handlers (and clients) as the calling user** — Parse Server embeds the caller's live session token in every trigger webhook fired by a logged-in user. A handler can now opt in to acting on the server *as that user* — full ACL/CLP/`protectedFields` enforcement, no master key. `payload.session_token` exposes the captured token (`nil` for master-key requests; still scrubbed from `payload.user`/`payload.object`/`as_json`/logs); `payload.user_agent` returns a client-mode `Parse::Agent`, and `payload.user_client` a non-master `Parse::Client` with the token **bound** so even raw REST calls authorize as the user. The same user-scoped client is available client-side via `Parse::User#session_client` and the `Parse::Client#become(token)` primitive, with `Parse::Client#with_session { … }` for block scoping. Backed by a new `Parse::Client.new(session_token:)` option. See [Acting as the calling user](#acting-as-the-calling-user)
26
26
  - **5.3.0 — Pluralized class-name aliases** — referencing the plural form of a model constant now resolves to that class, so `Posts.where(:author.eq => user).count` works for a class `Post`. The alias is created lazily on first reference and is the *same class object*, so every class method (`query`/`where`, `count`, `find`, `all`, scopes) works through it and `Posts.parse_class` still returns `"Post"`. Because it is the same class it adds no `Parse::Object.descendants` entry and never registers a separate Parse schema class. Classes whose name already ends in `s` are skipped by the automatic path; non-Parse plurals and typos fall through to a normal `NameError`. On by default — opt out with `Parse.pluralized_aliases = false` (or `PARSE_PLURALIZED_ALIASES=false`). For a custom plural, an `s`-ending class, or a namespaced model, call `pluralized_alias!` in the class body. See [Pluralized class-name aliases](#pluralized-class-name-aliases)
27
27
  - **5.3.0 — afterSave create reports changed fields; force_ssl-consistent file equality** — a trigger handler that keys off dirty tracking now sees every field on an `afterSave` *create*, symmetric with `afterSave` updates: the built object marks each populated data property changed (from `nil`) while `createdAt`/`updatedAt`/`ACL`/`objectId` stay clean and object readability, `new?`, and `existed?` are unchanged — so a handler that builds a payload from `*_changed?` / `changes` works uniformly across create and update. Separately, `Parse::File#==` now compares both files through the canonical `url` reader, so two files at the same location compare equal regardless of `Parse::File.force_ssl` (and `a == b` matches `b == a`), and a re-signed URL for the same object no longer reads as a change. See [Cloud Code Triggers](#cloud-code-triggers)
28
28
 
29
- ### What's new in 5.2
29
+ ## What's new in 5.2
30
30
 
31
31
  - **5.2.1 — Webhook triggers receive the full Parse object** — trigger handlers (`beforeSave`/`afterSave`/…) now get the complete server object (`createdAt`/`updatedAt`, `ACL`, internal fields); only live credentials (session tokens, password hashes) are stripped. `Parse::Object#existed?` / `#new?` are reliable in `afterSave`, `afterSave` updates carry dirty tracking, and the model lifecycle runs in ActiveModel order — `before_save → before_create` then `after_create → after_save` — so `before_create` now fires for REST/JS/Auth0 creates (and `after_save` no longer double-fires). See [Cloud Code Triggers](#cloud-code-triggers)
32
32
  - **Retrieval layer — `Parse::Retrieval` (`Parse::RAG`)** — `Parse::Retrieval.retrieve(query:, klass:, k:, filter:, tenant_scope:, …)` embeds a natural-language query, runs Atlas `$vectorSearch` through the existing ACL-enforcing `find_similar`, and splits each retrieved document's text field into scored `Parse::Retrieval::Chunk`s. Chunking is presentation-only (embedding stays one-vector-per-record), via `Parse::Retrieval::Chunker::FixedSizeOverlap(size:, overlap:, by:, max_chunks_per_document:)` (subclass `Chunker::Base` for custom strategies). ACL is mongo-direct (no REST two-stage); tenant scope folds into the Atlas pre-filter
@@ -40,7 +40,7 @@ A full-featured Ruby client SDK for [Parse Server](http://parseplatform.org/). [
40
40
 
41
41
  See [CHANGELOG.md](./CHANGELOG.md) for the full 5.2 entry.
42
42
 
43
- ### What's new in 5.1
43
+ ## What's new in 5.1
44
44
 
45
45
  - **`Parse::File` URL normalization + presigned-URL stash** — `Parse::File#url=` and `attributes=` now strip signed-URL query parameters (`X-Amz-Signature`, `AWSAccessKeyId`, `Key-Pair-Id`, etc.) before storage; the bare canonical URL lands in `@url`, and the original signed URL is stashed in `file.presigned_url` with a data-driven expiry in `file.presigned_url_expires_at`. New `file.presigned_url_valid?(buffer: 60)` predicate, configurable `Parse::File.signed_url_policy = :strip | :raise`, and `Parse::File.log_filter` / `log_filter_strict` regexes for `lograge` / Sentry / Honeybadger scrubbers. `Parse::File#inspect` no longer emits the URL — see CHANGELOG for the error-reporter payload migration callout
46
46
  - **`Parse::Lock` — public TTL-bounded mutual-exclusion primitive** — `Parse::Lock.acquire(key, ttl:, wait:) { … }` exposes the Redis-backed lock previously hidden inside `first_or_create!` as a first-class API. In-process `Mutex` fallback for memory-backed caches, fails closed on backend errors, HMAC-keyed via `PARSE_STACK_LOCK_SECRET`, namespace-separated from `first_or_create!` so the two cannot collide
@@ -56,7 +56,7 @@ See [CHANGELOG.md](./CHANGELOG.md) for the full 5.2 entry.
56
56
 
57
57
  See [CHANGELOG.md](./CHANGELOG.md) for the full 5.1 entry, including breaking changes, migration callouts, and the round-by-round security review notes.
58
58
 
59
- ### What's new in 5.0
59
+ ## What's new in 5.0
60
60
 
61
61
  - **RAG foundation** — `:vector` property type, `Parse::Embeddings` provider registry shipping built-in adapters for OpenAI, Cohere (v3 + v4.0 Matryoshka text-mode), Voyage (incl. open-weight `voyage-4-nano` and `voyage-multimodal-3` text-mode), Jina v3/v4/v5/code, Qwen 3 (DashScope), and a generic `LocalHTTP` client for Ollama / LM Studio / vLLM / TEI. `Klass.find_similar(vector:/text:, k:)` over Atlas `$vectorSearch`, and an `embed` class macro that digest-tracks source fields so vectors only recompute when content changes
62
62
  - **`Parse::Cache::Redis`** — Moneta-compatible Redis cache wrapper with a built-in `ConnectionPool`, optional `cache_namespace:` for multi-tenant Redis sharing, and graceful degrade on pool saturation
@@ -64,12 +64,12 @@ See [CHANGELOG.md](./CHANGELOG.md) for the full 5.1 entry, including breaking ch
64
64
  - **MCP transport hardening** — Streamable HTTP `Mcp-Session-Id` header (renamed from `X-MCP-Session-Id`, **breaking**), `MCP-Protocol-Version` validation, `DELETE /` session termination, structured-content (`outputSchema`) on built-in tools, optional `health_path:` liveness probe
65
65
  - **`Parse::GraphQL::TypeGenerator`** — generate `graphql-ruby` types directly from your `Parse::Object` subclasses (no Parse Server round-trip), with `:vector` columns surfaced as `[Float]` and association registries (`has_one_associations`, `has_many_associations`) populated at DSL time
66
66
  - **LiveQuery promoted to stable** — the experimental warning is removed; `Parse.live_query_enabled = true` is retained as a network-egress safety toggle, not a stability gate
67
- - **Server-version deprecation warning** — one-shot warning when connecting to Parse Server below the supported floor (currently 7.0.0); silence with `Parse.suppress_server_version_warning = true`
67
+ - **Server-version deprecation warning** — one-shot warning when connecting to a Parse Server older than the configured threshold (default `7.0.0`, override with `PARSE_DEPRECATED_SERVER_VERSION_BELOW`); silence with `Parse.suppress_server_version_warning = true`. The **supported baseline is Parse Server 9.x** (the SDK is developed and tested against a pinned `parse-server:9.9.0`); the default warning threshold is intentionally conservative so older deployments only get an advisory, not a hard break.
68
68
  - **`mongo_relation_index :field, dedup: true`** — register a compound `{owningId, relatedId}` UNIQUE on relation join collections to prevent duplicate-pair subscriptions without breaking `has_many` semantics
69
69
 
70
70
  See [CHANGELOG.md](./CHANGELOG.md) for the full 5.0 entry, including security-hardening notes and Ruby 3.x cleanup.
71
71
 
72
- ### Core capabilities
72
+ ## Core capabilities
73
73
 
74
74
  > **Vector search requires MongoDB Atlas (or Atlas Local).** The `:vector`
75
75
  > property, `find_similar`, `hybrid_search`, and `Parse::Retrieval` all
@@ -104,16 +104,16 @@ Below is a [quick start guide](#overview). See also the [Usage Guide](./docs/usa
104
104
 
105
105
  > **Note:** API reference docs are published at [neurosynq.github.io/parse-stack-next](https://neurosynq.github.io/parse-stack-next/index.html). Generated via YARD from the current source; covers the full 5.x surface.
106
106
 
107
- ### Credits
107
+ ## Credits
108
108
 
109
109
  This project (`parse-stack-next`) is a continuation of the [Parse Stack framework](https://github.com/modernistik/parse-stack) originally created by [Modernistik](https://www.modernistik.com). We are grateful for their foundational work and continue to build upon it under the [neurosynq](https://github.com/neurosynq) organization.
110
110
 
111
- ### Code Status
111
+ ## Code Status
112
112
  [![Gem Version](https://img.shields.io/gem/v/parse-stack-next.svg)](https://rubygems.org/gems/parse-stack-next)
113
113
  [![Downloads](https://img.shields.io/gem/dt/parse-stack-next.svg)](https://rubygems.org/gems/parse-stack-next)
114
114
  [![Releases](https://img.shields.io/github/v/release/neurosynq/parse-stack-next)](https://github.com/neurosynq/parse-stack-next/releases)
115
115
 
116
- #### Tutorial Videos
116
+ ### Tutorial Videos
117
117
 
118
118
  The following videos were recorded for the original parse-stack gem. The model, query, and association surface they cover is unchanged in parse-stack-next, so they remain a useful introduction; see the [Usage Guide](./docs/usage_guide.md) for v5.x-specific features (vector search, Redis cache, agent tools).
119
119
 
@@ -177,7 +177,7 @@ Parse.auto_generate_models!
177
177
  # or define custom Subclasses (Highly Recommended)
178
178
  class Song < Parse::Object
179
179
  property :name
180
- property :play, :integer
180
+ property :plays, :integer
181
181
  property :audio_file, :file
182
182
  property :tags, :array
183
183
  property :released, :date
@@ -248,7 +248,7 @@ Runnable, self-contained scripts live in [`examples/`](examples/) — see
248
248
 
249
249
  ## Release History
250
250
 
251
- **Current version: 5.4.0** | **Ruby 3.2+ required**
251
+ **Current version: 5.5.5** | **Ruby 3.2+ required**
252
252
 
253
253
  The 5.0 highlights (vector search / RAG, pooled Redis cache, AS::N instrumentation, MCP transport hardening, GraphQL type generation) are summarized in the [What's new in 5.0](#whats-new-in-50) section above. Earlier releases are recorded below.
254
254
 
@@ -294,6 +294,19 @@ Per-version detail lives in [CHANGELOG.md](./CHANGELOG.md) and on the [Releases
294
294
 
295
295
  The 1.x line is the original [`modernistik/parse-stack`](https://github.com/modernistik/parse-stack) — Active Model ORM, REST client, query DSL, associations, and Cloud Code webhooks for Parse Server. `parse-stack-next` is a continuation of that work; the first release published under the new gem name is **4.5.0** (above), on RubyGems as [`parse-stack-next`](https://rubygems.org/gems/parse-stack-next).
296
296
 
297
+ ## Guides
298
+
299
+ In-depth guides for the larger subsystems live under [`docs/`](./docs):
300
+
301
+ - [Usage Guide](./docs/usage_guide.md) — end-to-end tour of models, queries, and CRUD.
302
+ - [Client SDK Guide](./docs/client_sdk_guide.md) — running as an unprivileged (non-master) client: session tokens, `with_session`, row-level ACL enforcement, LiveQuery from the client side.
303
+ - [ACL & CLP Guide](./docs/acl_clp_guide.md) — access-control model, Class-Level Permissions, and how enforcement differs across REST vs mongo-direct.
304
+ - [Direct MongoDB Guide](./docs/mongodb_direct_guide.md) — the `results_direct` / `Parse::MongoDB` path, its authority model, and when to reach for it.
305
+ - [MongoDB Index Optimization Guide](./docs/mongodb_index_optimization_guide.md) — index DSL, schema migrators, and the writer-URI triple-gate.
306
+ - [Atlas & Vector Search Guide](./docs/atlas_vector_search_guide.md) — `$search`, vector search, `find_similar`, `hybrid_search`, embeddings, and RAG retrieval.
307
+ - [AI Agent & MCP Guide](./docs/mcp_guide.md) — `Parse::Agent`, the built-in tools, permission tiers, and the MCP server surface.
308
+ - [Webhooks Guide](./docs/webhooks_guide.md) — Cloud Code webhook receiver, triggers, field guards, and replay protection.
309
+
297
310
  ## Table of Contents
298
311
 
299
312
  <!-- START doctoc generated TOC please keep comment here to allow auto update -->
data/bin/parse-console CHANGED
@@ -76,7 +76,15 @@ opt_parser = OptionParser.new do |o|
76
76
  o.on('--url URL', 'Load the env config from a url.') do |url|
77
77
  begin
78
78
  puts "Loading config: #{url}"
79
- json = JSON.load open(url)
79
+ # SEC-20: do NOT pass a user-supplied string to bare Kernel#open
80
+ # `open("|command")` executes a subprocess. Parse an explicit URI and
81
+ # require an HTTP(S) scheme, then use open-uri's URI::HTTP#open (a real
82
+ # network fetch), never the Kernel form.
83
+ uri = URI.parse(url)
84
+ unless uri.is_a?(URI::HTTP) # URI::HTTPS < URI::HTTP, so this admits both
85
+ raise "Refusing to load config from a non-HTTP(S) URL: #{url.inspect}"
86
+ end
87
+ json = JSON.load(uri.open)
80
88
  raise "Contents not a JSON hash." unless json.is_a?(Hash)
81
89
  json.each { |k,v| ENV[k.upcase] = v }
82
90
  opts[:server_url] ||= ENV['PARSE_SERVER_URL']
data/docs/TEST_SERVER.md CHANGED
@@ -1,271 +1,148 @@
1
1
  # Parse Stack Test Server Setup
2
2
 
3
- This document explains how to set up a local Parse Server for testing the parse-stack Ruby SDK.
3
+ This document explains how to run the local, isolated Parse Server stack used
4
+ to exercise the parse-stack-next Ruby SDK's integration suite.
5
+
6
+ The stack is namespaced so it never collides with another Parse test system on
7
+ the same machine: a dedicated Compose project, a private `29xxx` host-port
8
+ block, dedicated container/volume names (`psnext-it-*`), loopback-only port
9
+ binds by default, and a dedicated database.
4
10
 
5
11
  ## Quick Start
6
12
 
7
- ### Option 1: Using Make (Recommended)
13
+ ### Option 1: Using Make
8
14
 
9
15
  ```bash
10
- # Start the test server
11
- make test-server-start
12
-
13
- # Test the connection
14
- make test-connection
15
-
16
- # Run integration tests
17
- make test-integration
18
-
19
- # Stop the test server
20
- make test-server-stop
16
+ make test-server-start # bring up the stack
17
+ make test-connection # verify connectivity
18
+ make test-integration # run the integration suite
19
+ make test-server-stop # tear the stack down
21
20
  ```
22
21
 
23
- ### Option 2: Docker Compose
22
+ ### Option 2: Docker Compose directly
24
23
 
25
- 1. **Start the test server:**
26
- ```bash
27
- docker-compose -f docker-compose.test.yml up -d
28
- ```
24
+ ```bash
25
+ # Start
26
+ docker-compose -f scripts/docker/docker-compose.test.yml up -d
29
27
 
30
- 2. **Test the connection:**
31
- ```bash
32
- ruby test_server_connection.rb
33
- ```
28
+ # Verify Parse Server is healthy
29
+ curl http://localhost:29337/parse/health # => {"status":"ok"}
34
30
 
35
- 3. **Run integration tests:**
36
- ```bash
37
- PARSE_TEST_USE_DOCKER=true bundle exec rake test
38
- ```
31
+ # Run integration tests
32
+ PARSE_TEST_USE_DOCKER=true bundle exec rake test:integration
39
33
 
40
- 4. **Stop the test server:**
41
- ```bash
42
- docker-compose -f docker-compose.test.yml down
43
- ```
34
+ # Stop
35
+ docker-compose -f scripts/docker/docker-compose.test.yml down
36
+ ```
44
37
 
45
- ### Option 3: Use Your Own Parse Server
38
+ ### Option 3: Use your own Parse Server
46
39
 
47
- Set environment variables to point to your Parse Server:
40
+ Point the suite at any Parse Server by exporting the client-side variables:
48
41
 
49
42
  ```bash
50
43
  export PARSE_TEST_SERVER_URL="http://your-server:1337/parse"
51
44
  export PARSE_TEST_APP_ID="your-app-id"
52
- export PARSE_TEST_API_KEY="your-rest-key"
45
+ export PARSE_TEST_API_KEY="your-rest-key"
53
46
  export PARSE_TEST_MASTER_KEY="your-master-key"
54
47
  ```
55
48
 
56
- ## Services Included
57
-
58
- The Docker Compose setup provides:
59
-
60
- - **MongoDB** (port 27017): Database backend
61
- - **Parse Server** (port 1337): Main API server with custom startup script
62
- - **Parse Dashboard** (port 4040): Web interface for data management
63
-
64
- ## Technical Implementation
65
-
66
- ### Custom Parse Server Image
67
-
68
- The setup uses a custom Docker image built on top of `parseplatform/parse-server:8.2.3` that includes:
69
-
70
- - **Custom startup script** (`scripts/start-parse.sh`) that sets the `PARSE_SERVER_MASTER_KEY_IPS` environment variable
71
- - **IP restriction bypass** allowing requests from any IP address (`0.0.0.0/0,::/0`)
72
- - **Automatic environment variable setup** for proper master key authentication
73
-
74
- ### Master Key Authentication
75
-
76
- The setup resolves Parse Server's IP restriction for master key usage by:
77
-
78
- 1. Using a custom Docker image with an embedded startup script
79
- 2. Setting `PARSE_SERVER_MASTER_KEY_IPS=0.0.0.0/0,::/0` to allow all IP addresses
80
- 3. This enables schema operations and full master key functionality for testing
81
-
82
- ### File Structure
83
-
84
- ```
85
- parse-stack-next/
86
- ├── scripts/
87
- │ ├── docker/
88
- │ │ ├── docker-compose.test.yml # Main Docker Compose configuration
89
- │ │ └── Dockerfile.parse # Custom Parse Server image
90
- │ ├── start-parse.sh # Startup script with environment setup
91
- │ └── test_server_connection.rb # Connection test script
92
- ├── config/
93
- │ └── parse-config.json # Parse Server configuration (unused)
94
- ├── test/
95
- │ ├── cloud/
96
- │ │ └── main.js # Cloud Code for testing
97
- │ └── support/
98
- │ ├── test_server.rb # Ruby test helper utilities
99
- │ └── docker_helper.rb # Docker container management
100
- └── .env.test # Environment variable defaults
101
- ```
102
-
103
- ## Test Configuration
104
-
105
- ### Environment Variables
106
-
107
- | Variable | Default | Description |
108
- |----------|---------|-------------|
109
- | `PARSE_TEST_SERVER_URL` | `http://localhost:1337/parse` | Parse Server URL |
110
- | `PARSE_TEST_APP_ID` | `myAppId` | Application ID |
111
- | `PARSE_TEST_API_KEY` | `test-rest-key` | REST API Key |
112
- | `PARSE_TEST_MASTER_KEY` | `myMasterKey` | Master Key |
113
- | `PARSE_TEST_USE_DOCKER` | `false` | Auto-manage Docker containers |
114
- | `PARSE_TEST_AUTO_START` | `false` | Start containers automatically |
115
- | `PARSE_TEST_AUTO_STOP` | `false` | Stop containers on exit |
116
-
117
- ### Using `.env.test`
118
-
119
- Copy and customize the test environment file:
120
-
121
- ```bash
122
- cp .env.test .env.test.local
123
- # Edit .env.test.local with your settings
124
- ```
125
-
126
- ## Writing Integration Tests
127
-
128
- ### Basic Setup
129
-
130
- ```ruby
131
- require_relative 'test_helper_integration'
132
-
133
- class MyIntegrationTest < Minitest::Test
134
- include ParseStackIntegrationTest
135
-
136
- def test_user_creation
137
- with_parse_server do
138
- user = create_test_user(username: 'testuser')
139
- assert user.id.present?
140
- assert_equal 'testuser', user.username
141
- end
142
- end
143
- end
144
- ```
145
-
146
- ### Test Helpers Available
147
-
148
- - `with_parse_server { }` - Skip test if server unavailable
149
- - `create_test_user(attributes)` - Create and track test user
150
- - `create_test_object(class_name, attributes)` - Create and track test object
151
- - `reset_database!` - Clear all non-system data
152
- - `@test_context.track(object)` - Track object for cleanup
153
-
154
- ### Manual Server Management
155
-
156
- ```ruby
157
- # In your tests or console
158
- require 'test/support/docker_helper'
159
-
160
- # Start containers
161
- Parse::Test::DockerHelper.start!
162
-
163
- # Check if running
164
- Parse::Test::DockerHelper.running?
165
-
166
- # View logs
167
- puts Parse::Test::DockerHelper.logs
168
-
169
- # Stop containers
170
- Parse::Test::DockerHelper.stop!
171
- ```
172
-
173
- ## Dashboard Access
174
-
175
- When using Docker Compose, you can access the Parse Dashboard at:
176
- - URL: http://localhost:4040
177
- - Username: `admin`
178
- - Password: `admin`
179
-
180
- ## Cloud Code Testing
181
-
182
- Sample cloud functions are provided in `test/cloud/main.js`:
183
-
184
- ```ruby
185
- # Test cloud functions
186
- result = Parse::CloudFunction.call('hello', name: 'World')
187
- assert_equal 'Hello World!', result
188
- ```
49
+ ## Services and ports
50
+
51
+ Every value has a baked-in default, so the stack is isolated even with no env
52
+ file present. Host ports live in the `29xxx` block and bind to `127.0.0.1` by
53
+ default.
54
+
55
+ | Service | Host port | Override | Container |
56
+ |-----------------|-----------|---------------------|-----------------------|
57
+ | Parse Server | 29337 | `PARSE_HOST_PORT` | `psnext-it-server` |
58
+ | MongoDB | 29017 | `MONGO_HOST_PORT` | `psnext-it-mongo` |
59
+ | Redis | 29379 | `REDIS_HOST_PORT` | `psnext-it-redis` |
60
+ | Parse Dashboard | 29040 | `DASHBOARD_HOST_PORT` | `psnext-it-dashboard` |
61
+
62
+ - **`PSNEXT_PREFIX`** (default `psnext-it`) names the Compose project and every
63
+ container. Set it (e.g. `PSNEXT_PREFIX=psnext-ci`) to run a second, fully
64
+ separate copy.
65
+ - **Versions**: Parse Server is pinned to `parseplatform/parse-server:9.9.0`
66
+ (see `scripts/docker/Dockerfile.parse`), MongoDB `mongo:8`, Redis
67
+ `redis:7-alpine`, Dashboard `parseplatform/parse-dashboard:9`.
68
+ - **Database**: Parse uses `parse_stack_next_it`.
69
+
70
+ ## Credentials (compose defaults)
71
+
72
+ | Setting | Default | Compose env |
73
+ |-------------|---------------------|-------------------------|
74
+ | App ID | `psnextItAppId` | `PARSE_APP_ID` |
75
+ | Master key | `psnextItMasterKey` | `PARSE_MASTER_KEY` |
76
+ | REST key | `psnext-it-rest-key`| `PARSE_API_KEY` |
77
+
78
+ These defaults are intentionally non-secret — they only ever bind to loopback.
79
+ Supply real values (and non-loopback binds) via your shell or a secret manager
80
+ if you point the stack at anything shared.
81
+
82
+ ## Security posture (not an anti-pattern)
83
+
84
+ The stack is hardened by construction, not opened up:
85
+
86
+ - **Loopback binds by default** — `PARSE_BIND` / `MONGO_BIND` / `REDIS_BIND` /
87
+ `DASHBOARD_BIND` all default to `127.0.0.1`, so nothing is published on the
88
+ LAN unless you explicitly override a bind.
89
+ - **Scoped master-key IPs** Parse Server's `masterKeyIps` is set to loopback
90
+ plus the private Docker ranges
91
+ (`127.0.0.1/32,::1/128,172.16.0.0/12,192.168.0.0/16,10.0.0.0/8`) so the
92
+ Ruby suite and the Dashboard container can use the master key, but it is
93
+ **not** opened to `0.0.0.0/0`.
94
+ - **Preflight guard** — a `preflight` service (`scripts/docker/preflight.sh`)
95
+ gates startup and refuses to bring the stack up on a non-loopback bind while
96
+ still using the default credentials, unless `ALLOW_INSECURE_BIND=1` or real
97
+ `PARSE_MASTER_KEY` / `MONGO_ROOT_PASSWORD` values are supplied.
98
+
99
+ ## Client-side test variables
100
+
101
+ The Ruby suite reads these (all have `29xxx` / `psnext-it` defaults):
102
+
103
+ | Variable | Purpose |
104
+ |--------------------------|--------------------------------------|
105
+ | `PARSE_TEST_SERVER_URL` | Parse Server URL (`http://localhost:29337/parse`) |
106
+ | `PARSE_TEST_APP_ID` | Application ID |
107
+ | `PARSE_TEST_API_KEY` | REST API key |
108
+ | `PARSE_TEST_MASTER_KEY` | Master key |
109
+ | `PARSE_TEST_MONGO_URI` | Mongo URI for mongo-direct tests |
110
+ | `PARSE_TEST_REDIS_URL` | Redis URL |
111
+ | `PARSE_TEST_LIVE_QUERY_URL` | LiveQuery WebSocket URL |
112
+ | `PARSE_TEST_USE_DOCKER` | Auto-manage the Docker stack |
113
+
114
+ `.env.test` is a committed reference listing the whole set;
115
+ `set -a; source .env.test; set +a` loads them all at once. Nothing auto-loads
116
+ it the baked-in defaults apply otherwise.
117
+
118
+ ## Dashboard
119
+
120
+ With the stack up, the Parse Dashboard is at <http://localhost:29040>.
189
121
 
190
122
  ## Troubleshooting
191
123
 
192
- ### Docker Issues
193
-
194
124
  ```bash
195
- # Check container status
196
- docker-compose -f docker-compose.test.yml ps
197
-
198
- # View Parse Server logs
199
- docker logs parse-stack-test-server
200
-
201
- # Reset everything
202
- docker-compose -f docker-compose.test.yml down -v
203
- docker-compose -f docker-compose.test.yml up -d
204
- ```
205
-
206
- ### Master Key Authentication Issues
207
-
208
- If you see `Request using master key rejected as the request IP address ... is not set in Parse Server option 'masterKeyIps'`:
209
-
210
- 1. **Verify the custom image is built**:
211
- ```bash
212
- docker-compose -f docker-compose.test.yml build parse
213
- ```
214
-
215
- 2. **Check startup script execution**:
216
- ```bash
217
- docker logs parse-stack-test-server | grep "PARSE_SERVER_MASTER_KEY_IPS"
218
- ```
219
- Should show: `PARSE_SERVER_MASTER_KEY_IPS: 0.0.0.0/0,::/0`
125
+ # Container status
126
+ docker-compose -f scripts/docker/docker-compose.test.yml ps
220
127
 
221
- 3. **Test master key directly**:
222
- ```bash
223
- curl -X GET \
224
- -H "X-Parse-Application-Id: myAppId" \
225
- -H "X-Parse-Master-Key: myMasterKey" \
226
- http://localhost:1337/parse/schemas
227
- ```
128
+ # Parse Server logs
129
+ docker logs psnext-it-server
228
130
 
229
- ### Connection Issues
230
-
231
- ```ruby
232
- # Test connectivity in console
233
- require 'test/support/test_server'
234
- Parse::Test::ServerHelper.setup
235
- Parse::Test::ServerHelper.server_available?
236
- ```
237
-
238
- ### Port Conflicts
239
-
240
- If ports 1337, 4040, or 27017 are in use, modify `docker-compose.test.yml`:
241
-
242
- ```yaml
243
- services:
244
- parse:
245
- ports:
246
- - "1338:1337" # Use port 1338 instead
131
+ # Full reset (clears volumes)
132
+ docker-compose -f scripts/docker/docker-compose.test.yml down -v
133
+ docker-compose -f scripts/docker/docker-compose.test.yml up -d
247
134
  ```
248
135
 
249
- Then update your environment variables accordingly.
250
-
251
- ## Production vs Test Differences
252
-
253
- The test server configuration includes:
254
- - Relaxed security settings for testing
255
- - Auto-creation of classes
256
- - Verbose logging
257
- - Sample cloud code
258
-
259
- **Never use these settings in production!**
136
+ ### Master-key IP rejection
260
137
 
261
- ## Status
138
+ If you see `Request using master key rejected as the request IP address ... is
139
+ not set in Parse Server option 'masterKeyIps'`, your client is reaching the
140
+ server from an address outside the scoped `masterKeyIps` list above. Run the
141
+ suite from the host (loopback) or from within the Docker network, rather than
142
+ widening `masterKeyIps`.
262
143
 
263
- **Working Setup**: This test server configuration has been verified to work with:
264
- - Parse Server 8.2.3
265
- - MongoDB 5.0
266
- - Master key authentication for schema operations
267
- - Basic CRUD operations via REST API
268
- - Ruby Parse Stack SDK connection
269
- - Cloud Code execution
144
+ ### Port conflicts
270
145
 
271
- The setup successfully resolves Parse Server's IP restriction issues that typically prevent master key usage in Docker environments.
146
+ Every host port is overridable via the `*_HOST_PORT` variables in the table
147
+ above (and the matching `PARSE_TEST_*` client variable). Move both the compose
148
+ side and the client side together so the containers and the suite agree.
data/docs/mcp_guide.md CHANGED
@@ -3844,7 +3844,7 @@ Tenant scope is applied as a `$match` stage at the TOP-level pipeline only. Sub-
3844
3844
  2. **Refuse lookups from tenant-bound agents entirely** — application code rejects pipelines containing `$lookup` / `$graphLookup` / `$unionWith` when `agent.tenant_id` is set.
3845
3845
  3. **Mark joinable cross-tenant classes as `agent_hidden`** — the most permissive joining-class is unreachable to the agent.
3846
3846
 
3847
- The proper fix (recursive scope injection into sub-pipelines) is tracked as a follow-up; see [SECURITY_GUIDE.md](../SECURITY_GUIDE.md) for the threat model and posture recommendations.
3847
+ The proper fix (recursive scope injection into sub-pipelines) is tracked as a follow-up; see [acl_clp_guide.md](./acl_clp_guide.md) and [SECURITY.md](../SECURITY.md) for the threat model and posture recommendations.
3848
3848
 
3849
3849
  ---
3850
3850
 
@@ -645,8 +645,9 @@ explicit mappings are correct for production.
645
645
  - [mongodb_direct_guide.md](./mongodb_direct_guide.md) — the full
646
646
  direct-Mongo / index-management API reference (DSL spelling,
647
647
  writer URI, triple-gate, rake tasks)
648
- - [SECURITY_GUIDE.md](../SECURITY_GUIDE.md) — security posture around
649
- the writer URI, role validation, audit trail
648
+ - [acl_clp_guide.md](./acl_clp_guide.md) — security posture around
649
+ the writer URI, role validation, and ACL/CLP enforcement on the
650
+ mongo-direct path
650
651
  - MongoDB official: <https://www.mongodb.com/docs/manual/indexes/>
651
652
  - Parse Server source for auto-managed indexes:
652
653
  <https://github.com/parse-community/parse-server/blob/master/src/Adapters/Storage/Mongo/MongoStorageAdapter.js>
data/docs/usage_guide.md CHANGED
@@ -626,6 +626,6 @@ end
626
626
 
627
627
  ## More Information
628
628
 
629
- - [CHANGELOG](./CHANGELOG.md) - Full feature history
629
+ - [CHANGELOG](../CHANGELOG.md) - Full feature history
630
630
  - [GitHub Releases](https://github.com/neurosynq/parse-stack-next/releases) - Release notes
631
631
  - [Parse Server Docs](https://docs.parseplatform.org) - Parse Server documentation