build/docs: architecture, deps
This commit is contained in:
@@ -1,5 +1,7 @@
|
|||||||
This is a Nostr server written using Elixir and PostgreSQL.
|
This is a Nostr server written using Elixir and PostgreSQL.
|
||||||
|
|
||||||
|
NOTE: Nostr and NIP specs are available in `~/nostr/` and `~/nips/`.
|
||||||
|
|
||||||
## Project guidelines
|
## Project guidelines
|
||||||
|
|
||||||
- Use `mix precommit` alias when you are done with all changes and fix any pending issues
|
- Use `mix precommit` alias when you are done with all changes and fix any pending issues
|
||||||
|
|||||||
86
PROGRESS.md
Normal file
86
PROGRESS.md
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
# PROGRESS (ephemeral)
|
||||||
|
|
||||||
|
Implementation checklist for Parrhesia relay.
|
||||||
|
|
||||||
|
## Phase 0 — foundation
|
||||||
|
|
||||||
|
- [ ] Confirm architecture doc with final NIP scope (`docs/ARCH.md`)
|
||||||
|
- [ ] Add core deps (websocket/http server, ecto_sql/postgrex, telemetry, test tooling)
|
||||||
|
- [ ] Establish application config structure (limits, policies, feature flags)
|
||||||
|
- [ ] Wire initial supervision tree skeleton
|
||||||
|
|
||||||
|
## Phase 1 — protocol core (NIP-01)
|
||||||
|
|
||||||
|
- [ ] Implement websocket endpoint + per-connection process
|
||||||
|
- [ ] Implement message decode/encode for `EVENT`, `REQ`, `CLOSE`
|
||||||
|
- [ ] Implement strict event validation (`id`, `sig`, shape, timestamps)
|
||||||
|
- [ ] Implement filter evaluation engine (AND/OR semantics)
|
||||||
|
- [ ] Implement subscription lifecycle + `EOSE` behavior
|
||||||
|
- [ ] Implement canonical `OK`, `NOTICE`, `CLOSED` responses + prefixes
|
||||||
|
|
||||||
|
## Phase 2 — storage boundary + postgres adapter
|
||||||
|
|
||||||
|
- [ ] Define `Parrhesia.Storage.*` behaviors (events/moderation/groups/admin)
|
||||||
|
- [ ] Implement Postgres adapter modules behind behaviors
|
||||||
|
- [ ] Create migrations for events, tags, moderation, membership
|
||||||
|
- [ ] Implement replaceable/addressable semantics at storage layer
|
||||||
|
- [ ] Add adapter contract test suite
|
||||||
|
|
||||||
|
## Phase 3 — fanout + performance primitives
|
||||||
|
|
||||||
|
- [ ] Build ETS-backed subscription index
|
||||||
|
- [ ] Implement candidate narrowing by kind/author/tag
|
||||||
|
- [ ] Add bounded outbound queues/backpressure per connection
|
||||||
|
- [ ] Add telemetry for ingest/query/fanout latency + queue depth
|
||||||
|
|
||||||
|
## Phase 4 — relay metadata and auth
|
||||||
|
|
||||||
|
- [ ] NIP-11 endpoint (`application/nostr+json`)
|
||||||
|
- [ ] NIP-42 challenge/auth flow
|
||||||
|
- [ ] Enforce NIP-70 protected events (default reject, auth override)
|
||||||
|
- [ ] Add auth-required/restricted response paths for writes and reqs
|
||||||
|
|
||||||
|
## Phase 5 — lifecycle and moderation features
|
||||||
|
|
||||||
|
- [ ] NIP-09 deletion requests
|
||||||
|
- [ ] NIP-40 expiration handling + purge worker
|
||||||
|
- [ ] NIP-62 vanish requests (hard delete semantics)
|
||||||
|
- [ ] NIP-13 PoW gate (configurable minimum)
|
||||||
|
- [ ] Moderation tables + policy hooks (ban/allow/event/ip)
|
||||||
|
|
||||||
|
## Phase 6 — query extensions
|
||||||
|
|
||||||
|
- [ ] NIP-45 `COUNT` (exact)
|
||||||
|
- [ ] Optional HLL response support
|
||||||
|
- [ ] NIP-50 search (`search` filter + ranking)
|
||||||
|
- [ ] NIP-77 negentropy (`NEG-OPEN/MSG/CLOSE`)
|
||||||
|
|
||||||
|
## Phase 7 — private messaging, groups, and MLS
|
||||||
|
|
||||||
|
- [ ] NIP-17/59 recipient-protected giftwrap read path (`kind:1059`)
|
||||||
|
- [ ] NIP-29 group event policy + relay metadata events
|
||||||
|
- [ ] NIP-43 membership request flow (`28934/28935/28936`, `8000/8001`, `13534`)
|
||||||
|
- [ ] NIP-EE (feature-flagged): `443`, `445`, `10051` handling
|
||||||
|
- [ ] MLS retention policy + tests for commit race edge cases
|
||||||
|
|
||||||
|
## Phase 8 — management API + operations
|
||||||
|
|
||||||
|
- [ ] NIP-86 HTTP management endpoint
|
||||||
|
- [ ] NIP-98 auth validation for management calls
|
||||||
|
- [ ] Implement supported management methods + audit logging
|
||||||
|
- [ ] Build health/readiness and Prometheus-compatible `/metrics` endpoints
|
||||||
|
|
||||||
|
## Phase 9 — full test + hardening pass
|
||||||
|
|
||||||
|
- [ ] Unit + integration + property test coverage for all critical modules
|
||||||
|
- [ ] End-to-end websocket conformance scenarios
|
||||||
|
- [ ] Load/soak tests with target p95 latency budgets
|
||||||
|
- [ ] Fault-injection tests (DB outages, high churn, restart recovery)
|
||||||
|
- [ ] Final precommit run and fix all issues
|
||||||
|
|
||||||
|
## Nice-to-have / backlog
|
||||||
|
|
||||||
|
- [ ] Multi-node fanout via PG LISTEN/NOTIFY or external bus
|
||||||
|
- [ ] Partitioned event storage + archival strategy
|
||||||
|
- [ ] Alternate storage adapter prototype (non-Postgres)
|
||||||
|
- [ ] Compatibility mode for Marmot protocol transition
|
||||||
253
docs/ARCH.md
Normal file
253
docs/ARCH.md
Normal file
@@ -0,0 +1,253 @@
|
|||||||
|
# Parrhesia Nostr Relay Architecture
|
||||||
|
|
||||||
|
## 1) Goals
|
||||||
|
|
||||||
|
Build a **robust, high-performance Nostr relay** in Elixir/OTP with PostgreSQL as first adapter, while keeping a strict boundary so storage can be swapped later.
|
||||||
|
|
||||||
|
Primary targets:
|
||||||
|
|
||||||
|
- Broad relay feature support (core + modern relay-facing NIPs)
|
||||||
|
- Strong correctness around NIP-01 semantics
|
||||||
|
- Clear OTP supervision and failure isolation
|
||||||
|
- High fanout throughput and bounded resource usage
|
||||||
|
- Storage abstraction via behavior-driven ports/adapters
|
||||||
|
- Full test suite (unit, integration, conformance, perf, fault-injection)
|
||||||
|
- Support for experimental MLS flow (NIP-EE), behind feature flags
|
||||||
|
|
||||||
|
## 2) NIP support scope
|
||||||
|
|
||||||
|
### Mandatory baseline
|
||||||
|
|
||||||
|
- NIP-01 (includes behavior moved from NIP-12/NIP-16/NIP-20/NIP-33)
|
||||||
|
- NIP-11 (relay info document)
|
||||||
|
|
||||||
|
### Relay-facing features to include
|
||||||
|
|
||||||
|
- NIP-09 (deletion requests)
|
||||||
|
- NIP-13 (PoW gating)
|
||||||
|
- NIP-17 + NIP-44 + NIP-59 (private DMs / gift wraps)
|
||||||
|
- NIP-40 (expiration)
|
||||||
|
- NIP-42 (AUTH)
|
||||||
|
- NIP-43 (relay membership requests/metadata)
|
||||||
|
- NIP-45 (COUNT, optional HLL)
|
||||||
|
- NIP-50 (search)
|
||||||
|
- NIP-62 (request to vanish)
|
||||||
|
- NIP-66 (relay discovery events; store/serve as normal events)
|
||||||
|
- NIP-70 (protected events)
|
||||||
|
- NIP-77 (negentropy sync)
|
||||||
|
- NIP-86 + NIP-98 (relay management API auth)
|
||||||
|
|
||||||
|
### Experimental MLS
|
||||||
|
|
||||||
|
- NIP-EE (unrecommended/upstream-superseded, but requested):
|
||||||
|
- kind `443` KeyPackage events
|
||||||
|
- kind `445` group events (policy-controlled retention/ephemeral treatment)
|
||||||
|
- kind `10051` keypackage relay lists
|
||||||
|
- interop with wrapped delivery (`1059`) and auth/privacy policies
|
||||||
|
|
||||||
|
## 3) System architecture (high level)
|
||||||
|
|
||||||
|
```text
|
||||||
|
WS/HTTP Edge (Bandit/Plug)
|
||||||
|
-> Protocol Decoder/Encoder
|
||||||
|
-> Command Router (EVENT/REQ/CLOSE/AUTH/COUNT/NEG-*)
|
||||||
|
-> Policy Pipeline (validation, auth, ACL, PoW, NIP-70)
|
||||||
|
-> Event Service / Query Service
|
||||||
|
-> Storage Port (behavior)
|
||||||
|
-> Postgres Adapter (Ecto)
|
||||||
|
-> Subscription Index (ETS)
|
||||||
|
-> Fanout Dispatcher
|
||||||
|
-> Telemetry + Metrics + Tracing
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4) OTP supervision design
|
||||||
|
|
||||||
|
`Parrhesia.Application` children (top-level):
|
||||||
|
|
||||||
|
1. `Parrhesia.Telemetry` – metric definitions/reporters
|
||||||
|
2. `Parrhesia.Config` – runtime config cache (ETS-backed)
|
||||||
|
3. `Parrhesia.Storage.Supervisor` – adapter processes (`Repo`, pools)
|
||||||
|
4. `Parrhesia.Subscriptions.Supervisor` – subscription index + fanout workers
|
||||||
|
5. `Parrhesia.Auth.Supervisor` – AUTH challenge/session tracking
|
||||||
|
6. `Parrhesia.Policy.Supervisor` – rate limiters / ACL caches
|
||||||
|
7. `Parrhesia.Web.Endpoint` – WS + HTTP ingress
|
||||||
|
8. `Parrhesia.Tasks.Supervisor` – background jobs (expiry purge, maintenance)
|
||||||
|
|
||||||
|
Failure model:
|
||||||
|
|
||||||
|
- Connection failures are isolated per socket process.
|
||||||
|
- Storage outages degrade with explicit `OK/CLOSED` error prefixes (`error:`) per NIP-01.
|
||||||
|
- Non-critical workers are `:transient`; core infra is `:permanent`.
|
||||||
|
|
||||||
|
## 5) Core runtime components
|
||||||
|
|
||||||
|
### 5.1 Connection process
|
||||||
|
|
||||||
|
Per websocket connection:
|
||||||
|
|
||||||
|
- Parse frames, enforce max frame/message limits
|
||||||
|
- Maintain authenticated pubkeys (NIP-42)
|
||||||
|
- Track active subscriptions (`sub_id` scoped to connection)
|
||||||
|
- Handle backpressure (bounded outbound queue + drop/close strategy)
|
||||||
|
|
||||||
|
### 5.2 Command router
|
||||||
|
|
||||||
|
Dispatches:
|
||||||
|
|
||||||
|
- `EVENT` -> ingest pipeline
|
||||||
|
- `REQ` -> initial DB query + live subscription
|
||||||
|
- `CLOSE` -> unsubscribe
|
||||||
|
- `AUTH` -> challenge validation, session update
|
||||||
|
- `COUNT` -> aggregate path
|
||||||
|
- `NEG-OPEN`/`NEG-MSG`/`NEG-CLOSE` -> negentropy session engine
|
||||||
|
|
||||||
|
### 5.3 Event ingest pipeline
|
||||||
|
|
||||||
|
Ordered stages:
|
||||||
|
|
||||||
|
1. Decode + schema checks
|
||||||
|
2. `id` recomputation and signature verification
|
||||||
|
3. NIP semantic checks (timestamps, tag forms, size limits)
|
||||||
|
4. Policy checks (banlists, kind allowlists, auth-required, NIP-70, PoW)
|
||||||
|
5. Storage write (or no-store for ephemeral policy)
|
||||||
|
6. Live fanout to matching subscriptions
|
||||||
|
7. Return canonical `OK` response with machine prefix when needed
|
||||||
|
|
||||||
|
### 5.4 Subscription index + fanout
|
||||||
|
|
||||||
|
- ETS-backed inverted indices (`kind`, `author`, single-letter tags)
|
||||||
|
- Candidate narrowing before full filter evaluation
|
||||||
|
- OR semantics across filters, AND within filter
|
||||||
|
- `limit` only for initial query phase; ignored in live phase (NIP-01)
|
||||||
|
|
||||||
|
### 5.5 Query service
|
||||||
|
|
||||||
|
- Compiles NIP filters into adapter-neutral query AST
|
||||||
|
- Pushes AST to storage adapter
|
||||||
|
- Deterministic ordering (`created_at` desc, `id` lexical tie-break)
|
||||||
|
- Emits `EOSE` exactly once per subscription initial catch-up
|
||||||
|
|
||||||
|
## 6) Storage boundary (swap-friendly by design)
|
||||||
|
|
||||||
|
### 6.1 Port/adapter contract
|
||||||
|
|
||||||
|
Define behaviors under `Parrhesia.Storage`:
|
||||||
|
|
||||||
|
- `Parrhesia.Storage.Events`
|
||||||
|
- `put_event/2`, `get_event/2`, `query/3`, `count/3`
|
||||||
|
- `delete_by_request/2`, `vanish/2`, `purge_expired/1`
|
||||||
|
- `Parrhesia.Storage.Moderation`
|
||||||
|
- pubkey/event bans, allowlists, blocked IPs
|
||||||
|
- `Parrhesia.Storage.Groups`
|
||||||
|
- NIP-29/NIP-43 membership + role operations
|
||||||
|
- `Parrhesia.Storage.Admin`
|
||||||
|
- backing for NIP-86 methods
|
||||||
|
|
||||||
|
All domain logic depends only on these behaviors.
|
||||||
|
|
||||||
|
### 6.2 Postgres adapter notes
|
||||||
|
|
||||||
|
Initial adapter: `Parrhesia.Storage.Adapters.Postgres` with Ecto.
|
||||||
|
|
||||||
|
Schema outline:
|
||||||
|
|
||||||
|
- `events` (id PK, pubkey, created_at, kind, content, sig, d_tag, deleted_at, expires_at)
|
||||||
|
- `event_tags` (event_id, name, value, idx)
|
||||||
|
- moderation tables (banned/allowed pubkeys, banned events, blocked IPs)
|
||||||
|
- relay/group membership tables
|
||||||
|
- optional count/HLL helper tables
|
||||||
|
|
||||||
|
Indexing strategy:
|
||||||
|
|
||||||
|
- `(kind, created_at DESC)`
|
||||||
|
- `(pubkey, created_at DESC)`
|
||||||
|
- `(created_at DESC)`
|
||||||
|
- `(name, value, created_at DESC)` on `event_tags`
|
||||||
|
- partial/unique indexes for replaceable and addressable semantics
|
||||||
|
|
||||||
|
Retention strategy:
|
||||||
|
|
||||||
|
- Optional table partitioning by time for hot pruning
|
||||||
|
- Periodic purge job for expired/deleted tombstoned rows
|
||||||
|
|
||||||
|
## 7) Feature-specific implementation notes
|
||||||
|
|
||||||
|
### 7.1 NIP-11
|
||||||
|
|
||||||
|
- Serve on WS URL with `Accept: application/nostr+json`
|
||||||
|
- Include accurate `supported_nips` and `limitation`
|
||||||
|
|
||||||
|
### 7.2 NIP-42 + NIP-70
|
||||||
|
|
||||||
|
- Connection-scoped challenge store
|
||||||
|
- Protected (`["-"]`) events rejected by default unless auth+pubkey match
|
||||||
|
|
||||||
|
### 7.3 NIP-17/59 privacy guardrails
|
||||||
|
|
||||||
|
- Relay can enforce recipient-only reads for kind `1059` (AUTH required)
|
||||||
|
- Query path validates requester access for wrapped DM fetches
|
||||||
|
|
||||||
|
### 7.4 NIP-45 COUNT
|
||||||
|
|
||||||
|
- Exact count baseline
|
||||||
|
- Optional approximate mode and HLL payloads for common queries
|
||||||
|
|
||||||
|
### 7.5 NIP-50 search
|
||||||
|
|
||||||
|
- Use Postgres FTS (`tsvector`) with ranking
|
||||||
|
- Apply `limit` after ranking
|
||||||
|
|
||||||
|
### 7.6 NIP-77 negentropy
|
||||||
|
|
||||||
|
- Track per-negentropy-session state in dedicated GenServer
|
||||||
|
- Use bounded resources + inactivity timeout
|
||||||
|
|
||||||
|
### 7.7 NIP-62 vanish
|
||||||
|
|
||||||
|
- Hard-delete all events by pubkey up to `created_at`
|
||||||
|
- Also delete matching gift wraps where feasible (`#p` target)
|
||||||
|
- Persist minimal audit record if needed for operations/legal trace
|
||||||
|
|
||||||
|
### 7.8 NIP-EE MLS (feature-flagged)
|
||||||
|
|
||||||
|
- Accept/store kind `443` KeyPackage events
|
||||||
|
- Process kind `445` under configurable retention policy (default short TTL)
|
||||||
|
- Ensure kind `10051` replaceable semantics
|
||||||
|
- Keep relay MLS-agnostic cryptographically (no MLS decryption in relay path)
|
||||||
|
|
||||||
|
## 8) Performance model
|
||||||
|
|
||||||
|
- Bounded mailbox and queue limits on connections
|
||||||
|
- ETS-heavy hot path (subscription match, auth/session cache)
|
||||||
|
- DB writes batched where safe; reads via prepared plans
|
||||||
|
- Avoid global locks; prefer partitioned workers and sharded ETS tables
|
||||||
|
- Telemetry-first tuning: p50/p95/p99 for ingest, query, fanout
|
||||||
|
- Expose Prometheus-compatible `/metrics` endpoint for scraping
|
||||||
|
|
||||||
|
Targets (initial):
|
||||||
|
|
||||||
|
- p95 EVENT ack < 50ms under nominal load
|
||||||
|
- p95 REQ initial response start < 120ms on indexed queries
|
||||||
|
- predictable degradation under overload via rate-limit + backpressure
|
||||||
|
|
||||||
|
## 9) Testing strategy (full suite)
|
||||||
|
|
||||||
|
1. **Unit tests**: parser, filter evaluator, policy predicates, NIP validators
|
||||||
|
2. **Property tests**: filter semantics, replaceable/addressable conflict resolution
|
||||||
|
3. **Adapter contract tests**: shared behavior tests run against Postgres adapter
|
||||||
|
4. **Integration tests**: websocket protocol flows (`EVENT/REQ/CLOSE/AUTH/COUNT/NEG-*`)
|
||||||
|
5. **NIP conformance tests**: machine-prefix responses, ordering, EOSE behavior
|
||||||
|
6. **MLS scenario tests**: keypackage/group-event acceptance and policy handling
|
||||||
|
7. **Performance tests**: soak + burst + large fanout profiles
|
||||||
|
8. **Fault-injection tests**: DB outage, slow query, connection churn, node restart
|
||||||
|
|
||||||
|
## 10) Implementation principles
|
||||||
|
|
||||||
|
- Keep relay event-kind agnostic by default; special-case only where NIPs require
|
||||||
|
- Prefer explicit feature flags for expensive/experimental modules
|
||||||
|
- No direct Ecto usage outside Postgres adapter and migration layer
|
||||||
|
- Every feature lands with tests + telemetry hooks
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Implementation task breakdown is tracked in `./PROGRESS.md`.
|
||||||
23
mix.exs
23
mix.exs
@@ -21,11 +21,28 @@ defmodule Parrhesia.MixProject do
|
|||||||
# Run "mix help deps" to learn about dependencies.
|
# Run "mix help deps" to learn about dependencies.
|
||||||
defp deps do
|
defp deps do
|
||||||
[
|
[
|
||||||
{:ecto, "~> 3.0"},
|
# Runtime: web + protocol edge
|
||||||
|
{:bandit, "~> 1.5"},
|
||||||
|
{:plug, "~> 1.15"},
|
||||||
|
|
||||||
|
# Runtime: storage adapter (Postgres first)
|
||||||
|
{:ecto_sql, "~> 3.12"},
|
||||||
|
{:postgrex, ">= 0.0.0"},
|
||||||
|
|
||||||
|
# Runtime: telemetry + prometheus exporter (/metrics)
|
||||||
|
{:telemetry_metrics, "~> 1.0"},
|
||||||
|
{:telemetry_poller, "~> 1.0"},
|
||||||
|
{:telemetry_metrics_prometheus, "~> 1.1"},
|
||||||
|
|
||||||
|
# Test tooling
|
||||||
|
{:stream_data, "~> 1.0", only: :test},
|
||||||
|
{:mox, "~> 1.1", only: :test},
|
||||||
|
{:bypass, "~> 2.1", only: :test},
|
||||||
|
{:websockex, "~> 0.4", only: :test},
|
||||||
|
|
||||||
|
# Project tooling
|
||||||
{:deps_changelog, "~> 0.3"},
|
{:deps_changelog, "~> 0.3"},
|
||||||
{:igniter, "~> 0.6", only: [:dev, :test]}
|
{:igniter, "~> 0.6", only: [:dev, :test]}
|
||||||
# {:dep_from_hexpm, "~> 0.3.0"},
|
|
||||||
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
|
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
22
mix.lock
22
mix.lock
@@ -1,7 +1,14 @@
|
|||||||
%{
|
%{
|
||||||
|
"bandit": {:hex, :bandit, "1.10.3", "1e5d168fa79ec8de2860d1b4d878d97d4fbbe2fdbe7b0a7d9315a4359d1d4bb9", [:mix], [{:hpax, "~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.18", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "99a52d909c48db65ca598e1962797659e3c0f1d06e825a50c3d75b74a5e2db18"},
|
||||||
|
"bypass": {:hex, :bypass, "2.1.0", "909782781bf8e20ee86a9cabde36b259d44af8b9f38756173e8f5e2e1fabb9b1", [:mix], [{:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: false]}, {:ranch, "~> 1.3", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "d9b5df8fa5b7a6efa08384e9bbecfe4ce61c77d28a4282f79e02f1ef78d96b80"},
|
||||||
|
"cowboy": {:hex, :cowboy, "2.14.2", "4008be1df6ade45e4f2a4e9e2d22b36d0b5aba4e20b0a0d7049e28d124e34847", [:make, :rebar3], [{:cowlib, ">= 2.16.0 and < 3.0.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, ">= 1.8.0 and < 3.0.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "569081da046e7b41b5df36aa359be71a0c8874e5b9cff6f747073fc57baf1ab9"},
|
||||||
|
"cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"},
|
||||||
|
"cowlib": {:hex, :cowlib, "2.16.0", "54592074ebbbb92ee4746c8a8846e5605052f29309d3a873468d76cdf932076f", [:make, :rebar3], [], "hexpm", "7f478d80d66b747344f0ea7708c187645cfcc08b11aa424632f78e25bf05db51"},
|
||||||
|
"db_connection": {:hex, :db_connection, "2.9.0", "a6a97c5c958a2d7091a58a9be40caf41ab496b0701d21e1d1abff3fa27a7f371", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "17d502eacaf61829db98facf6f20808ed33da6ccf495354a41e64fe42f9c509c"},
|
||||||
"decimal": {:hex, :decimal, "2.3.0", "3ad6255aa77b4a3c4f818171b12d237500e63525c2fd056699967a3e7ea20f62", [:mix], [], "hexpm", "a4d66355cb29cb47c3cf30e71329e58361cfcb37c34235ef3bf1d7bf3773aeac"},
|
"decimal": {:hex, :decimal, "2.3.0", "3ad6255aa77b4a3c4f818171b12d237500e63525c2fd056699967a3e7ea20f62", [:mix], [], "hexpm", "a4d66355cb29cb47c3cf30e71329e58361cfcb37c34235ef3bf1d7bf3773aeac"},
|
||||||
"deps_changelog": {:hex, :deps_changelog, "0.3.5", "65981997d9bc893b8027a0c03da093a4083328c00b17f562df269c2b61d44073", [:mix], [], "hexpm", "298fcd7794395d8e61dba8d29ce8fcee09f1df4d48adb273a41e8f4a1736491e"},
|
"deps_changelog": {:hex, :deps_changelog, "0.3.5", "65981997d9bc893b8027a0c03da093a4083328c00b17f562df269c2b61d44073", [:mix], [], "hexpm", "298fcd7794395d8e61dba8d29ce8fcee09f1df4d48adb273a41e8f4a1736491e"},
|
||||||
"ecto": {:hex, :ecto, "3.13.5", "9d4a69700183f33bf97208294768e561f5c7f1ecf417e0fa1006e4a91713a834", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "df9efebf70cf94142739ba357499661ef5dbb559ef902b68ea1f3c1fabce36de"},
|
"ecto": {:hex, :ecto, "3.13.5", "9d4a69700183f33bf97208294768e561f5c7f1ecf417e0fa1006e4a91713a834", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "df9efebf70cf94142739ba357499661ef5dbb559ef902b68ea1f3c1fabce36de"},
|
||||||
|
"ecto_sql": {:hex, :ecto_sql, "3.13.5", "2f8282b2ad97bf0f0d3217ea0a6fff320ead9e2f8770f810141189d182dc304e", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.13.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.7", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.19 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "aa36751f4e6a2b56ae79efb0e088042e010ff4935fc8684e74c23b1f49e25fdc"},
|
||||||
"finch": {:hex, :finch, "0.21.0", "b1c3b2d48af02d0c66d2a9ebfb5622be5c5ecd62937cf79a88a7f98d48a8290c", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.6.2 or ~> 1.7", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "87dc6e169794cb2570f75841a19da99cfde834249568f2a5b121b809588a4377"},
|
"finch": {:hex, :finch, "0.21.0", "b1c3b2d48af02d0c66d2a9ebfb5622be5c5ecd62937cf79a88a7f98d48a8290c", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.6.2 or ~> 1.7", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "87dc6e169794cb2570f75841a19da99cfde834249568f2a5b121b809588a4377"},
|
||||||
"glob_ex": {:hex, :glob_ex, "0.1.11", "cb50d3f1ef53f6ca04d6252c7fde09fd7a1cf63387714fe96f340a1349e62c93", [:mix], [], "hexpm", "342729363056e3145e61766b416769984c329e4378f1d558b63e341020525de4"},
|
"glob_ex": {:hex, :glob_ex, "0.1.11", "cb50d3f1ef53f6ca04d6252c7fde09fd7a1cf63387714fe96f340a1349e62c93", [:mix], [], "hexpm", "342729363056e3145e61766b416769984c329e4378f1d558b63e341020525de4"},
|
||||||
"hpax": {:hex, :hpax, "1.0.3", "ed67ef51ad4df91e75cc6a1494f851850c0bd98ebc0be6e81b026e765ee535aa", [:mix], [], "hexpm", "8eab6e1cfa8d5918c2ce4ba43588e894af35dbd8e91e6e55c817bca5847df34a"},
|
"hpax": {:hex, :hpax, "1.0.3", "ed67ef51ad4df91e75cc6a1494f851850c0bd98ebc0be6e81b026e765ee535aa", [:mix], [], "hexpm", "8eab6e1cfa8d5918c2ce4ba43588e894af35dbd8e91e6e55c817bca5847df34a"},
|
||||||
@@ -9,13 +16,28 @@
|
|||||||
"jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
|
"jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
|
||||||
"mime": {:hex, :mime, "2.0.7", "b8d739037be7cd402aee1ba0306edfdef982687ee7e9859bee6198c1e7e2f128", [:mix], [], "hexpm", "6171188e399ee16023ffc5b76ce445eb6d9672e2e241d2df6050f3c771e80ccd"},
|
"mime": {:hex, :mime, "2.0.7", "b8d739037be7cd402aee1ba0306edfdef982687ee7e9859bee6198c1e7e2f128", [:mix], [], "hexpm", "6171188e399ee16023ffc5b76ce445eb6d9672e2e241d2df6050f3c771e80ccd"},
|
||||||
"mint": {:hex, :mint, "1.7.1", "113fdb2b2f3b59e47c7955971854641c61f378549d73e829e1768de90fc1abf1", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "fceba0a4d0f24301ddee3024ae116df1c3f4bb7a563a731f45fdfeb9d39a231b"},
|
"mint": {:hex, :mint, "1.7.1", "113fdb2b2f3b59e47c7955971854641c61f378549d73e829e1768de90fc1abf1", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "fceba0a4d0f24301ddee3024ae116df1c3f4bb7a563a731f45fdfeb9d39a231b"},
|
||||||
|
"mox": {:hex, :mox, "1.2.0", "a2cd96b4b80a3883e3100a221e8adc1b98e4c3a332a8fc434c39526babafd5b3", [:mix], [{:nimble_ownership, "~> 1.0", [hex: :nimble_ownership, repo: "hexpm", optional: false]}], "hexpm", "c7b92b3cc69ee24a7eeeaf944cd7be22013c52fcb580c1f33f50845ec821089a"},
|
||||||
"nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"},
|
"nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"},
|
||||||
|
"nimble_ownership": {:hex, :nimble_ownership, "1.0.2", "fa8a6f2d8c592ad4d79b2ca617473c6aefd5869abfa02563a77682038bf916cf", [:mix], [], "hexpm", "098af64e1f6f8609c6672127cfe9e9590a5d3fcdd82bc17a377b8692fd81a879"},
|
||||||
"nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"},
|
"nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"},
|
||||||
"owl": {:hex, :owl, "0.13.0", "26010e066d5992774268f3163506972ddac0a7e77bfe57fa42a250f24d6b876e", [:mix], [{:ucwidth, "~> 0.2", [hex: :ucwidth, repo: "hexpm", optional: true]}], "hexpm", "59bf9d11ce37a4db98f57cb68fbfd61593bf419ec4ed302852b6683d3d2f7475"},
|
"owl": {:hex, :owl, "0.13.0", "26010e066d5992774268f3163506972ddac0a7e77bfe57fa42a250f24d6b876e", [:mix], [{:ucwidth, "~> 0.2", [hex: :ucwidth, repo: "hexpm", optional: true]}], "hexpm", "59bf9d11ce37a4db98f57cb68fbfd61593bf419ec4ed302852b6683d3d2f7475"},
|
||||||
|
"plug": {:hex, :plug, "1.19.1", "09bac17ae7a001a68ae393658aa23c7e38782be5c5c00c80be82901262c394c0", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "560a0017a8f6d5d30146916862aaf9300b7280063651dd7e532b8be168511e62"},
|
||||||
|
"plug_cowboy": {:hex, :plug_cowboy, "2.8.0", "07789e9c03539ee51bb14a07839cc95aa96999fd8846ebfd28c97f0b50c7b612", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "9cbfaaf17463334ca31aed38ea7e08a68ee37cabc077b1e9be6d2fb68e0171d0"},
|
||||||
|
"plug_crypto": {:hex, :plug_crypto, "2.1.1", "19bda8184399cb24afa10be734f84a16ea0a2bc65054e23a62bb10f06bc89491", [:mix], [], "hexpm", "6470bce6ffe41c8bd497612ffde1a7e4af67f36a15eea5f921af71cf3e11247c"},
|
||||||
|
"postgrex": {:hex, :postgrex, "0.22.0", "fb027b58b6eab1f6de5396a2abcdaaeb168f9ed4eccbb594e6ac393b02078cbd", [:mix], [{:db_connection, "~> 2.9", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "a68c4261e299597909e03e6f8ff5a13876f5caadaddd0d23af0d0a61afcc5d84"},
|
||||||
|
"ranch": {:hex, :ranch, "1.8.1", "208169e65292ac5d333d6cdbad49388c1ae198136e4697ae2f474697140f201c", [:make, :rebar3], [], "hexpm", "aed58910f4e21deea992a67bf51632b6d60114895eb03bb392bb733064594dd0"},
|
||||||
"req": {:hex, :req, "0.5.17", "0096ddd5b0ed6f576a03dde4b158a0c727215b15d2795e59e0916c6971066ede", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "0b8bc6ffdfebbc07968e59d3ff96d52f2202d0536f10fef4dc11dc02a2a43e39"},
|
"req": {:hex, :req, "0.5.17", "0096ddd5b0ed6f576a03dde4b158a0c727215b15d2795e59e0916c6971066ede", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "0b8bc6ffdfebbc07968e59d3ff96d52f2202d0536f10fef4dc11dc02a2a43e39"},
|
||||||
"rewrite": {:hex, :rewrite, "1.3.0", "67448ba7975690b35ba7e7f35717efcce317dbd5963cb0577aa7325c1923121a", [:mix], [{:glob_ex, "~> 0.1", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.0", [hex: :sourceror, repo: "hexpm", optional: false]}, {:text_diff, "~> 0.1", [hex: :text_diff, repo: "hexpm", optional: false]}], "hexpm", "d111ac7ff3a58a802ef4f193bbd1831e00a9c57b33276e5068e8390a212714a5"},
|
"rewrite": {:hex, :rewrite, "1.3.0", "67448ba7975690b35ba7e7f35717efcce317dbd5963cb0577aa7325c1923121a", [:mix], [{:glob_ex, "~> 0.1", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.0", [hex: :sourceror, repo: "hexpm", optional: false]}, {:text_diff, "~> 0.1", [hex: :text_diff, repo: "hexpm", optional: false]}], "hexpm", "d111ac7ff3a58a802ef4f193bbd1831e00a9c57b33276e5068e8390a212714a5"},
|
||||||
"sourceror": {:hex, :sourceror, "1.12.0", "da354c5f35aad3cc1132f5d5b0d8437d865e2661c263260480bab51b5eedb437", [:mix], [], "hexpm", "755703683bd014ebcd5de9acc24b68fb874a660a568d1d63f8f98cd8a6ef9cd0"},
|
"sourceror": {:hex, :sourceror, "1.12.0", "da354c5f35aad3cc1132f5d5b0d8437d865e2661c263260480bab51b5eedb437", [:mix], [], "hexpm", "755703683bd014ebcd5de9acc24b68fb874a660a568d1d63f8f98cd8a6ef9cd0"},
|
||||||
"spitfire": {:hex, :spitfire, "0.3.10", "19aea9914132456515e8f7d592f63ab9f3130876b0252e834d2390bdd8becb24", [:mix], [], "hexpm", "6a6a5f77eb4165249c76199cd2d01fb595bac9207aed3de551918ac1c2bc9267"},
|
"spitfire": {:hex, :spitfire, "0.3.10", "19aea9914132456515e8f7d592f63ab9f3130876b0252e834d2390bdd8becb24", [:mix], [], "hexpm", "6a6a5f77eb4165249c76199cd2d01fb595bac9207aed3de551918ac1c2bc9267"},
|
||||||
|
"stream_data": {:hex, :stream_data, "1.3.0", "bde37905530aff386dea1ddd86ecbf00e6642dc074ceffc10b7d4e41dfd6aac9", [:mix], [], "hexpm", "3cc552e286e817dca43c98044c706eec9318083a1480c52ae2688b08e2936e3c"},
|
||||||
"telemetry": {:hex, :telemetry, "1.4.1", "ab6de178e2b29b58e8256b92b382ea3f590a47152ca3651ea857a6cae05ac423", [:rebar3], [], "hexpm", "2172e05a27531d3d31dd9782841065c50dd5c3c7699d95266b2edd54c2dafa1c"},
|
"telemetry": {:hex, :telemetry, "1.4.1", "ab6de178e2b29b58e8256b92b382ea3f590a47152ca3651ea857a6cae05ac423", [:rebar3], [], "hexpm", "2172e05a27531d3d31dd9782841065c50dd5c3c7699d95266b2edd54c2dafa1c"},
|
||||||
|
"telemetry_metrics": {:hex, :telemetry_metrics, "1.1.0", "5bd5f3b5637e0abea0426b947e3ce5dd304f8b3bc6617039e2b5a008adc02f8f", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "e7b79e8ddfde70adb6db8a6623d1778ec66401f366e9a8f5dd0955c56bc8ce67"},
|
||||||
|
"telemetry_metrics_prometheus": {:hex, :telemetry_metrics_prometheus, "1.1.0", "1cc23e932c1ef9aa3b91db257ead31ea58d53229d407e059b29bb962c1505a13", [:mix], [{:plug_cowboy, "~> 2.1", [hex: :plug_cowboy, repo: "hexpm", optional: false]}, {:telemetry_metrics_prometheus_core, "~> 1.0", [hex: :telemetry_metrics_prometheus_core, repo: "hexpm", optional: false]}], "hexpm", "d43b3659b3244da44fe0275b717701542365d4519b79d9ce895b9719c1ce4d26"},
|
||||||
|
"telemetry_metrics_prometheus_core": {:hex, :telemetry_metrics_prometheus_core, "1.2.1", "c9755987d7b959b557084e6990990cb96a50d6482c683fb9622a63837f3cd3d8", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "5e2c599da4983c4f88a33e9571f1458bf98b0cf6ba930f1dc3a6e8cf45d5afb6"},
|
||||||
|
"telemetry_poller": {:hex, :telemetry_poller, "1.3.0", "d5c46420126b5ac2d72bc6580fb4f537d35e851cc0f8dbd571acf6d6e10f5ec7", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "51f18bed7128544a50f75897db9974436ea9bfba560420b646af27a9a9b35211"},
|
||||||
"text_diff": {:hex, :text_diff, "0.1.0", "1caf3175e11a53a9a139bc9339bd607c47b9e376b073d4571c031913317fecaa", [:mix], [], "hexpm", "d1ffaaecab338e49357b6daa82e435f877e0649041ace7755583a0ea3362dbd7"},
|
"text_diff": {:hex, :text_diff, "0.1.0", "1caf3175e11a53a9a139bc9339bd607c47b9e376b073d4571c031913317fecaa", [:mix], [], "hexpm", "d1ffaaecab338e49357b6daa82e435f877e0649041ace7755583a0ea3362dbd7"},
|
||||||
|
"thousand_island": {:hex, :thousand_island, "1.4.3", "2158209580f633be38d43ec4e3ce0a01079592b9657afff9080d5d8ca149a3af", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "6e4ce09b0fd761a58594d02814d40f77daff460c48a7354a15ab353bb998ea0b"},
|
||||||
|
"websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"},
|
||||||
|
"websockex": {:hex, :websockex, "0.5.1", "9de28d37bbe34f371eb46e29b79c94c94fff79f93c960d842fbf447253558eb4", [:mix], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "8ef39576ed56bc3804c9cd8626f8b5d6b5721848d2726c0ccd4f05385a3c9f14"},
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user