eulgqvoalfmj6148itjvoos6as9q2s2v · 137 rows
| id | content_hash | title | description | design | acceptance_criteria | notes | status | priority | issue_type | assignee | estimated_minutes | created_at | created_by | owner | updated_at | closed_at | closed_by_session | external_ref | spec_id | compaction_level | compacted_at | compacted_at_commit | original_size | sender | ephemeral | wisp_type | pinned | is_template | mol_type | work_type | source_system | metadata | source_repo | close_reason | event_kind | actor | target | payload | await_type | await_id | timeout_ns | waiters | hook_bead | role_bead | agent_state | last_activity | role_type | rig | due_at | defer_until | no_history | started_at | is_blocked |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| tarantool-etcd-nwl | 3f54509527d06071750dbdddfdcafe415133385b9549ce08b80b06cc16f9d6cf | iproto-verify: etcd_user_revoke_role vs Auth.UserRevokeRole | Add Lua testkit verification for etcd_user_revoke_role (mirrors gRPC Auth.UserRevokeRole). Successful path: assert response shape matches the gRPC response message after MsgPack round-trip. Error path: at least one error case asserting {code, message} matches app/etcd/errors.lua catalog. Exercise at least one non-default option if applicable. Goes in test/iproto_parity_test.lua under group iproto/parity/auth. Call shape: local resp = etcd_user_revoke_role(req). | Verification-only. Wrapper exists at app/etcd/api.lua. Epic tarantool-etcd-w8q. | open | 3 | task | NULL | NULL | 2026-05-19T14:54:11Z | Eugene Blikh | bigbes@gmail.com | 2026-05-19T14:54:11Z | NULL | NULL | 0 | NULL | NULL | NULL | 0 | 0 | 0 | <binary> | 0 | NULL | NULL | NULL | 0 | NULL | 0 | |||||||||||||||||||||||
| tarantool-etcd-ovo | b02eee9877621d78af7f66286e7ff63fc6564aac6beeca0cb795fe667217cb17 | Learner: durable applied_index (WAL-equivalent for restart) | Persist enough raft state (currentTerm/HardState, applied_index, ConfState) so a restart resumes from the last applied entry instead of replaying from zero or forcing a full snapshot. Store in a dedicated space (e.g. etcd_raft_meta) written in the same transaction as each applied entry. On boot in learner mode: read last applied_index, reconnect transport, and request entries after it (or a snapshot if the leader has compacted past it). Tarantool's own WAL gives us durability of the *space writes*; this issue is specifically the raft bookkeeping that lets us re-attach to the cluster correctly. | etcd stores this as raft WAL + snapshot metadata; we collapse it to a meta space since Tarantool's WAL already durably persists the applied data. | open | 2 | task | NULL | NULL | 2026-05-20T06:45:17Z | Eugene Blikh | bigbes@gmail.com | 2026-07-17T14:27:37Z | NULL | NULL | 0 | NULL | NULL | NULL | 0 | 0 | 0 | <binary> | 0 | NULL | NULL | NULL | 0 | NULL | 1 | |||||||||||||||||||||||
| tarantool-etcd-ovt | db6cf24009cca490a9e5a89f705f27d73388ddfcc3513448452e373522faeeb5 | iproto-verify: etcd_member_remove vs Cluster.MemberRemove | Add Lua testkit verification for etcd_member_remove (mirrors gRPC etcdserverpb.Cluster.MemberRemove). Same test pattern as verify-only tasks: positive path plus at least one error path, response shape matches gRPC, error codes from app/etcd/errors.lua. For UNIMPLEMENTED-by-design endpoints (Cluster.MemberAdd/Remove/Update/Promote), assert the wrapper returns the canonical UNIMPLEMENTED error consistent with gRPC rather than skipping. Goes in test/iproto_parity_test.lua under group iproto/parity/cluster. | Epic tarantool-etcd-w8q. Blocked on the matching impl issue. | open | 3 | task | NULL | NULL | 2026-05-19T14:54:21Z | Eugene Blikh | bigbes@gmail.com | 2026-07-17T14:27:37Z | NULL | NULL | 0 | NULL | NULL | NULL | 0 | 0 | 0 | <binary> | 0 | NULL | NULL | NULL | 0 | NULL | 1 | |||||||||||||||||||||||
| tarantool-etcd-ozm | 88152e03bc9011e822aa42ffc9212e11e04a9788bd5d40d5312f3e47da59657c | iproto-verify: etcd_user_list vs Auth.UserList | Add Lua testkit verification for etcd_user_list (mirrors gRPC Auth.UserList). Successful path: assert response shape matches the gRPC response message after MsgPack round-trip. Error path: at least one error case asserting {code, message} matches app/etcd/errors.lua catalog. Exercise at least one non-default option if applicable. Goes in test/iproto_parity_test.lua under group iproto/parity/auth. Call shape: local resp = etcd_user_list(req). | Verification-only. Wrapper exists at app/etcd/api.lua. Epic tarantool-etcd-w8q. | open | 3 | task | NULL | NULL | 2026-05-19T14:54:09Z | Eugene Blikh | bigbes@gmail.com | 2026-05-19T14:54:09Z | NULL | NULL | 0 | NULL | NULL | NULL | 0 | 0 | 0 | <binary> | 0 | NULL | NULL | NULL | 0 | NULL | 0 | |||||||||||||||||||||||
| tarantool-etcd-p3s | d7ab326ea50217052ff7528067d9bda449d1d177009210c5b6dc67df52f33a52 | iproto-verify: etcd_election_leader vs Election.Leader | Add Lua testkit verification for etcd_election_leader (mirrors gRPC v3electionpb.Election.Leader). Same test pattern as verify-only tasks: positive path plus at least one error path, response shape matches gRPC, error codes from app/etcd/errors.lua. For UNIMPLEMENTED-by-design endpoints (Cluster.MemberAdd/Remove/Update/Promote), assert the wrapper returns the canonical UNIMPLEMENTED error consistent with gRPC rather than skipping. Goes in test/iproto_parity_test.lua under group iproto/parity/election. | Epic tarantool-etcd-w8q. Blocked on the matching impl issue. | open | 3 | task | NULL | NULL | 2026-05-19T14:54:48Z | Eugene Blikh | bigbes@gmail.com | 2026-07-17T14:27:37Z | NULL | NULL | 0 | NULL | NULL | NULL | 0 | 0 | 0 | <binary> | 0 | NULL | NULL | NULL | 0 | NULL | 1 | |||||||||||||||||||||||
| tarantool-etcd-q5f | 722c317df5aebe05f32f99bf226d4b916604f1518089bea768b2ed206866ecd3 | Learner: snapshot receive (bbolt parse + bucket translation) | Handle /raft/snapshot. When we fall too far behind (or on first join), the leader ships a bbolt DB file dump, NOT raft entries. Two options: (a) parse the bbolt file and translate etcd's buckets — key (the mvcc keyspace), meta (consistent_index, scheduledCompactRev, finishedCompactRev), lease, auth/authUsers/authRoles, members/members_removed, cluster, alarm — into our etcd_* spaces; or (b) refuse snapshots and bootstrap via clientv3 Range/Watch instead, accepting that the cluster MAY force a snapshot mid-stream if we lag past its compaction window (it eventually will). Recommend (a) for a true learner. Needs a minimal bbolt page/leaf reader in Lua or via FFI. This is the phase most likely to surprise — it bypasses the raft proto entirely. | bbolt format: page-based B+tree, freelist, meta pages with txid. Bucket values for mvcc keys are mvccpb.KeyValue protobufs keyed by revision{main,sub}. This is the single biggest sub-effort in the epic. | open | 2 | task | NULL | NULL | 2026-05-20T06:45:16Z | Eugene Blikh | bigbes@gmail.com | 2026-07-17T14:27:37Z | NULL | NULL | 0 | NULL | NULL | NULL | 0 | 0 | 0 | <binary> | 0 | NULL | NULL | NULL | 0 | NULL | 1 | |||||||||||||||||||||||
| tarantool-etcd-qp9 | 1821d4049d7399c5a860e917014844d4b8ec8343d0ebed8371971c93410ce818 | MemberListRequest.linearizable decoded and dropped | clientv3 sets linearizable=true by default since 3.5; the call must fail when the member is partitioned from quorum. That is the entire point of the flag — it is a quorum probe, not a freshness hint. app/etcd/cluster.lua:81 M.member_list() takes NO request argument and always serves local box.info.replication. The Range-only linearizable bounce (grpc.lua:180,221) does not cover MemberList. Nuance: our payload is never actually stale (membership is config-static, not raft-driven), so the RESPONSE is right — but the guarantee is absent, so a partitioned node answers confidently where etcd would fail. False-positive health signal. | linearizable=true fails or bounces to leader when quorum is unavailable; linearizable=false keeps serving locally; test covers the partitioned case. | open | 3 | bug | NULL | NULL | 2026-07-17T13:21:27Z | Eugene Blikh | bigbes@gmail.com | 2026-07-17T13:21:27Z | NULL | NULL | 0 | NULL | NULL | NULL | 0 | 0 | 0 | <binary> | 0 | NULL | NULL | NULL | 0 | NULL | 0 | |||||||||||||||||||||||
| tarantool-etcd-rzh | 26beb784e16ef7c22030f27a08fe4e55b5e0453b6cd19af9df1ffd97a8bb3235 | iproto-impl: etcd_maintenance_status vs Maintenance.Status | Wrap the existing handler maintenance.status (already invoked by gRPC/JSON via defs.lua route etcdserverpb.Maintenance.Status) as a Lua global etcd_maintenance_status callable via iproto CALL. Mechanical change in app/etcd/api.lua: add rawset(_G, 'etcd_maintenance_status', wrap('maintenance', 'status')) under the appropriate service block; append 'etcd_maintenance_status' to the funcs table so box.schema.func.create plus guest grant runs at role apply; document the wrapper in the module-level LDoc comment. If Maintenance.Status returns UNIMPLEMENTED today, the wrapper still gets added so the iproto caller observes the same UNIMPLEMENTED error gRPC would return. Election.Observe is server-streaming over gRPC; iproto has no stream primitive, so propose a polling shape (observe_create/_pull/_cancel) mirroring the Watch precedent at api.lua:81-83. | etcd_maintenance_status is callable from net.box:call('etcd_maintenance_status', {req}) with no Lua error before handler dispatch. Successful request shape produces same successful response shape as gRPC. box.schema.func.create plus guest grant idempotent on role re-apply. | Epic tarantool-etcd-w8q. Handler at app/etcd/maintenance.lua. | open | 3 | task | NULL | NULL | 2026-05-19T14:54:32Z | Eugene Blikh | bigbes@gmail.com | 2026-07-17T14:27:36Z | NULL | NULL | 0 | NULL | NULL | NULL | 0 | 0 | 0 | <binary> | 0 | NULL | NULL | NULL | 0 | NULL | 0 | ||||||||||||||||||||||
| tarantool-etcd-rzr | 3973dcbaf27d8ffaf25bdad2afcfcbfeb9e6deb07cd8ecf62c5a6446247c3468 | nghttp2 unary send-path rewrite — close parallel KVGet gap, drop jit.off | Parallel KVGet on tarantool is ~70% slower than etcd at GOMAXPROCS=6 (12 688 ops/s vs 21 900 ops/s; p95 970 µs vs 405 µs — docs/BENCH.md "Linux x86_64 — KVGet matrix, 2026-05-15"). Root cause: per-Connection IO mutex in lib/nghttp2/grpc.lua (Connection:_locked) — nghttp2 sessions are not reentrant, so every fiber's submit_response + send_all + sock:write serializes. Etcd has no such constraint. Fix: pre-build HEADERS + DATA + trailers in pure Lua, skip nghttp2's C→Lua data-source callback on the unary send path, recover ~10–20% throughput lost to jit.off + drop the JIT pin entirely. | See memory project_nghttp2_send_callback_rewrite for the plan. Pre-frame DATA in Lua, avoid re-entering nghttp2 mid-send. Touch points: - lib/nghttp2/grpc.lua (Connection, _locked, send_response) - lib/nghttp2/ffi.lua (data-source callback signature) - M.recv/M.send/M.send_all/M.send_data currently pinned jit.off — un-pin once the callback re-entry is gone (memory feedback_nghttp2_session_not_reentrant explains the original panic). | 1. BenchmarkKVGetParallel/point closes the gap to within ~10% of etcd at GOMAXPROCS=6. 2. p95 latency under parallel load drops below 600 µs. 3. No bad-callback panics across 10 verbose runs (the regression test from commit d0fa396). 4. jit.off removed from M.recv/M.send/M.send_all/M.send_data. | Sources: docs/BENCH.md "Linux x86_64" + memory notes project_nghttp2_send_callback_rewrite, feedback_nghttp2_session_not_reentrant, feedback_nghttp2_dual_connection. | open | 2 | task | NULL | NULL | 2026-05-19T13:34:42Z | Eugene Blikh | bigbes@gmail.com | 2026-05-19T13:34:42Z | NULL | NULL | 0 | NULL | NULL | NULL | 0 | 0 | 0 | <binary> | 0 | NULL | NULL | NULL | 0 | NULL | 0 | |||||||||||||||||||||
| tarantool-etcd-rzs | 63c2e0e3f3c883b2db5734e851457a1f568e3a1ebb871de515f13411bd7ecbd9 | iproto-impl: etcd_member_update vs Cluster.MemberUpdate | Wrap the existing handler cluster.member_update (already invoked by gRPC/JSON via defs.lua route etcdserverpb.Cluster.MemberUpdate) as a Lua global etcd_member_update callable via iproto CALL. Mechanical change in app/etcd/api.lua: add rawset(_G, 'etcd_member_update', wrap('cluster', 'member_update')) under the appropriate service block; append 'etcd_member_update' to the funcs table so box.schema.func.create plus guest grant runs at role apply; document the wrapper in the module-level LDoc comment. If Cluster.MemberUpdate returns UNIMPLEMENTED today, the wrapper still gets added so the iproto caller observes the same UNIMPLEMENTED error gRPC would return. Election.Observe is server-streaming over gRPC; iproto has no stream primitive, so propose a polling shape (observe_create/_pull/_cancel) mirroring the Watch precedent at api.lua:81-83. | etcd_member_update is callable from net.box:call('etcd_member_update', {req}) with no Lua error before handler dispatch. Successful request shape produces same successful response shape as gRPC. box.schema.func.create plus guest grant idempotent on role re-apply. | Epic tarantool-etcd-w8q. Handler at app/etcd/cluster.lua. | open | 3 | task | NULL | NULL | 2026-05-19T14:54:23Z | Eugene Blikh | bigbes@gmail.com | 2026-07-17T14:27:36Z | NULL | NULL | 0 | NULL | NULL | NULL | 0 | 0 | 0 | <binary> | 0 | NULL | NULL | NULL | 0 | NULL | 0 | ||||||||||||||||||||||
| tarantool-etcd-sdt | ce9fb24507b45a493bd9ffc4a4ab22edafe46b9e41a95b2813bb6b2016b0ccc8 | iproto-verify: etcd_auth_status vs Auth.AuthStatus | Add Lua testkit verification for etcd_auth_status (mirrors gRPC Auth.AuthStatus). Successful path: assert response shape matches the gRPC response message after MsgPack round-trip. Error path: at least one error case asserting {code, message} matches app/etcd/errors.lua catalog. Exercise at least one non-default option if applicable. Goes in test/iproto_parity_test.lua under group iproto/parity/auth. Call shape: local resp = etcd_auth_status(req). | Verification-only. Wrapper exists at app/etcd/api.lua. Epic tarantool-etcd-w8q. | open | 3 | task | NULL | NULL | 2026-05-19T14:54:04Z | Eugene Blikh | bigbes@gmail.com | 2026-05-19T14:54:04Z | NULL | NULL | 0 | NULL | NULL | NULL | 0 | 0 | 0 | <binary> | 0 | NULL | NULL | NULL | 0 | NULL | 0 | |||||||||||||||||||||||
| tarantool-etcd-sir | 5cc0da191ba98b3f835cc3fcdc3e3f5ed44926cb471cb6df204b16f644957871 | JWT auth-token mode (--auth-token=jwt) — we only have simple tokens | etcd supports two auth-token modes: simple (an opaque token in a server-side table) and jwt (a signed, self-contained token the server verifies with a key, no server-side session state). etcd 3.7 leans further on JWT — 'clients may now set the JWT directly, offering more flexibility in authentication options.' We implement simple-token only: Auth.Authenticate mints an opaque token stored in the etcd_tokens space (truncated on auth disable, app/etcd/auth.lua). There is no JWT handling anywhere in app/etcd/. A client configured against a JWT-mode etcd, or one presenting a pre-issued JWT, gets rejected by us. This also matters for our own topology: simple tokens are server-local session state, so under a replicaset a token minted on one node is not honored by another unless etcd_tokens replicates and stays consistent. JWT is stateless and sidesteps that entirely. Scope: an auth.token config block selecting simple|jwt, JWT sign/verify (RS256 + HS256 at minimum) over the etcd claim set (username, revision), key material from config, and acceptance of a client-supplied JWT on both wires. | Needs a JWT implementation in Lua — check what Tarantool ships (digest for HMAC/SHA, crypto for RSA) before pulling a dependency. Note digest.base64_encode MIME-wraps at 76 chars by default; JWT needs {nowrap = true} and URL-safe alphabet. | Server boots in jwt mode; a token minted by Authenticate verifies on a different replicaset node without shared session state; a conformance cell runs the auth suite in jwt mode against both backends; simple mode stays the default and unchanged. | open | 2 | feature | NULL | NULL | 2026-07-17T11:30:45Z | Eugene Blikh | bigbes@gmail.com | 2026-07-17T11:30:45Z | NULL | NULL | 0 | NULL | NULL | NULL | 0 | 0 | 0 | <binary> | 0 | NULL | NULL | NULL | 0 | NULL | 0 | ||||||||||||||||||||||
| tarantool-etcd-sqv | e03dd24141b120acf0cc989ed396918ed61284c9962f1c036f30a262a51d39e6 | iproto-impl: etcd_election_proclaim vs Election.Proclaim | Wrap the existing handler election.proclaim (already invoked by gRPC/JSON via defs.lua route v3electionpb.Election.Proclaim) as a Lua global etcd_election_proclaim callable via iproto CALL. Mechanical change in app/etcd/api.lua: add rawset(_G, 'etcd_election_proclaim', wrap('election', 'proclaim')) under the appropriate service block; append 'etcd_election_proclaim' to the funcs table so box.schema.func.create plus guest grant runs at role apply; document the wrapper in the module-level LDoc comment. If Election.Proclaim returns UNIMPLEMENTED today, the wrapper still gets added so the iproto caller observes the same UNIMPLEMENTED error gRPC would return. Election.Observe is server-streaming over gRPC; iproto has no stream primitive, so propose a polling shape (observe_create/_pull/_cancel) mirroring the Watch precedent at api.lua:81-83. | etcd_election_proclaim is callable from net.box:call('etcd_election_proclaim', {req}) with no Lua error before handler dispatch. Successful request shape produces same successful response shape as gRPC. box.schema.func.create plus guest grant idempotent on role re-apply. | Epic tarantool-etcd-w8q. Handler at app/etcd/election.lua. | open | 3 | task | NULL | NULL | 2026-05-19T14:54:46Z | Eugene Blikh | bigbes@gmail.com | 2026-07-17T14:27:36Z | NULL | NULL | 0 | NULL | NULL | NULL | 0 | 0 | 0 | <binary> | 0 | NULL | NULL | NULL | 0 | NULL | 0 | ||||||||||||||||||||||
| tarantool-etcd-sy6 | ff088cd8c36175c942b8e80aa7a1bfa3bdaa64bf92a9ff46f8c736505ea0b302 | iproto-verify: etcd_watch_pull vs Watch.Watch-pull | Add Lua testkit verification for etcd_watch_pull (mirrors gRPC Watch.Watch-pull). Successful path: assert response shape matches the gRPC response message after MsgPack round-trip. Error path: at least one error case asserting {code, message} matches app/etcd/errors.lua catalog. Exercise at least one non-default option if applicable. Goes in test/iproto_parity_test.lua under group iproto/parity/watch. Call shape: local resp = etcd_watch_pull(req). | Verification-only. Wrapper exists at app/etcd/api.lua. Epic tarantool-etcd-w8q. | open | 3 | task | NULL | NULL | 2026-05-19T14:53:57Z | Eugene Blikh | bigbes@gmail.com | 2026-05-19T14:53:57Z | NULL | NULL | 0 | NULL | NULL | NULL | 0 | 0 | 0 | <binary> | 0 | NULL | NULL | NULL | 0 | NULL | 0 | |||||||||||||||||||||||
| tarantool-etcd-tf0 | 4410402a2773378d989b3453c6d566e9ab0eb85ec6a8ba9eb21801b478e96d73 | iproto-impl: etcd_maintenance_hash_kv vs Maintenance.HashKV | Wrap the existing handler maintenance.hash_kv (already invoked by gRPC/JSON via defs.lua route etcdserverpb.Maintenance.HashKV) as a Lua global etcd_maintenance_hash_kv callable via iproto CALL. Mechanical change in app/etcd/api.lua: add rawset(_G, 'etcd_maintenance_hash_kv', wrap('maintenance', 'hash_kv')) under the appropriate service block; append 'etcd_maintenance_hash_kv' to the funcs table so box.schema.func.create plus guest grant runs at role apply; document the wrapper in the module-level LDoc comment. If Maintenance.HashKV returns UNIMPLEMENTED today, the wrapper still gets added so the iproto caller observes the same UNIMPLEMENTED error gRPC would return. Election.Observe is server-streaming over gRPC; iproto has no stream primitive, so propose a polling shape (observe_create/_pull/_cancel) mirroring the Watch precedent at api.lua:81-83. | etcd_maintenance_hash_kv is callable from net.box:call('etcd_maintenance_hash_kv', {req}) with no Lua error before handler dispatch. Successful request shape produces same successful response shape as gRPC. box.schema.func.create plus guest grant idempotent on role re-apply. | Epic tarantool-etcd-w8q. Handler at app/etcd/maintenance.lua. | open | 3 | task | NULL | NULL | 2026-05-19T14:54:39Z | Eugene Blikh | bigbes@gmail.com | 2026-07-17T14:27:36Z | NULL | NULL | 0 | NULL | NULL | NULL | 0 | 0 | 0 | <binary> | 0 | NULL | NULL | NULL | 0 | NULL | 0 | ||||||||||||||||||||||
| tarantool-etcd-tt6 | 63510fbc9abb4568f1a181ec6515e1edededff7dbb3a0cf8e1f67e62b24c780d | Replica route: lease / auth state mirroring (scope v1 vs later) | KV is the v1 target; this issue scopes how non-KV state behaves in replica mode and what (if anything) v1 mirrors. Questions: - Leases: do we mirror upstream lease grants/TTLs/expiry so locally-served keys show correct lease attachment, or pass-through lease ops to upstream? At minimum the lease_id on replicated kvs must round-trip; full lease lifecycle mirroring may be later. - Auth: mirror upstream users/roles/permissions, or run replica reads unauthenticated/locally-authed? Decide whether auth is enforced on the replica or delegated. - Watch served to local clients: our Watch subsystem already reconstructs from kv_history, which the tail populates — confirm local watchers work transparently once KV sync lands. Output: a documented v1 scope (likely 'KV + lease_id round-trip only; auth+full-lease+ deferred') appended to docs/TODO.md, plus any follow-up issues. | Likely mostly a scoping/decision issue that spawns concrete follow-ups; keep v1 KV-only. | open | 2 | task | NULL | NULL | 2026-05-20T06:17:57Z | Eugene Blikh | bigbes@gmail.com | 2026-05-20T06:17:57Z | NULL | NULL | 0 | NULL | NULL | NULL | 0 | 0 | 0 | <binary> | 0 | NULL | NULL | NULL | 0 | NULL | 0 | |||||||||||||||||||||||
| tarantool-etcd-tzk | 3118f9aa62633997a681d45e16a91f84bed91eb8b469c292c08f094d08840e0d | Acked writes are not fsynced — shipped config.yaml has no wal section | etcd fdatasyncs the WAL before responding, so an acked write survives power loss. Our ack ORDERING is correct (box.commit() before the response — app/etcd/kv.lua:566-586, app/etcd/txn.lua:278-292), but the shipped config.yaml:30-34 has NO wal: section, so Tarantool defaults to wal_mode='write' (page cache, no fsync). An acked write survives a process crash but NOT power loss — weaker than etcd on the same wire ack. Telling detail: the bench harness explicitly sets WalMode="fsync" 'for symmetric durability' (tests/conformance/bench/benchharness/spawn.go:42-49). So our benchmarks measure fsync durability while the shipped default does not provide it. That asymmetry should be a decision, not an accident. Judgment call: arguably deployment config rather than a code gap — one line closes it. Filed so the choice is explicit either way. | Either wal.mode: fsync in config.yaml, or an explicit docs/TODO.md divergence stating the shipped default trades durability for throughput and why the bench harness differs. | open | 2 | bug | NULL | NULL | 2026-07-17T13:20:53Z | Eugene Blikh | bigbes@gmail.com | 2026-07-17T13:20:53Z | NULL | NULL | 0 | NULL | NULL | NULL | 0 | 0 | 0 | <binary> | 0 | NULL | NULL | NULL | 0 | NULL | 0 | |||||||||||||||||||||||
| tarantool-etcd-ubj | 9fa2b019230fef524e54fc2a1286772300f5f2b92da91b3bef00be8c5e8c8613 | No auto-compaction (--auto-compaction-mode / --auto-compaction-retention) | etcd advances compact_revision autonomously via --auto-compaction-mode=periodic|revision plus --auto-compaction-retention. Client-driven Compact is correct (app/etcd/compact.lua:28) but there is NO background fiber and NO config knob — zero hits for 'auto.?compact|retention' across app/, lib/, docs/. app/roles/etcd.lua:6-44 has no compaction block. Impact: history grows without bound unless an external actor calls Compact. A drop-in etcd replacement configured with auto-compaction gets none, and the operator has no signal — this is the kind of gap that surfaces as unbounded memory growth in production weeks later. | Background fiber + roles_cfg.compaction.{mode,retention}. Interacts with the compact-revision boundary bug — land that first or the auto-compactor will mass-produce the watch livelock. | periodic and revision modes both advance compact_revision; configurable via roles_cfg; disabled by default matching etcd; test covers both modes. | open | 2 | feature | NULL | NULL | 2026-07-17T13:20:52Z | Eugene Blikh | bigbes@gmail.com | 2026-07-17T13:20:52Z | NULL | NULL | 0 | NULL | NULL | NULL | 0 | 0 | 0 | <binary> | 0 | NULL | NULL | NULL | 0 | NULL | 0 | ||||||||||||||||||||||
| tarantool-etcd-uee | 9e6ca4342149c79eee16a063f881ae3eb1683b9de5774eae58d2b37bfc8bcb9b | Replica route: bootstrap snapshot import | On entering replica mode, pull the full upstream keyspace once and load it into our etcd_* spaces, recording the upstream revision the snapshot was taken at (the Watch tail starts from here). Approach: a clientv3-style Range over the whole keyspace ([\\x00, \\x00) with WithRange/WithFromKey) reading at a single revision (use the header.revision from the first Range, or RangeRequest with revision pinned + paging via WithLimit + WithMinModRev/last-key continuation). Persist each kv with its create_revision/mod_revision/version/lease intact. Alternatively consume Maintenance.Snapshot if/when interop lands (tarantool-etcd-16d) — but Range is the dependency-free path for v1. Output: etcd_* spaces populated, and the 'synced through revision R' watermark persisted for the tail phase to resume from. | Must preserve upstream create_revision/mod_revision/version exactly — feeds revision-alignment. Page large keyspaces to avoid one giant Range. | open | 2 | task | NULL | NULL | 2026-05-20T06:17:54Z | Eugene Blikh | bigbes@gmail.com | 2026-07-17T14:27:37Z | NULL | NULL | 0 | NULL | NULL | NULL | 0 | 0 | 0 | <binary> | 0 | NULL | NULL | NULL | 0 | NULL | 1 | |||||||||||||||||||||||
| tarantool-etcd-umc | d8474c3796303d77baae0a958775f27a81b09ad2e0afaa4a00084cdf0ab6b7e9 | Learner: config block + role plumbing | Add the role config surface for learner mode and wire validate/apply/stop in app/roles/etcd.lua. New block under app.roles.etcd: learner: enabled: false cluster_peers: ['http://etcd-a:2380', ...] # PEER (2380) URLs, not client (2379) advertise_peer_url: 'http://this-node:2380' # how the cluster reaches us peer_tls: {...} Validate: when learner.enabled, the local store refuses normal client write registration; cluster_peers + advertise_peer_url non-empty; peer URLs are well-formed. No raft/transport logic here — just config parsing, validation, and the on/off switch the other phases hang off. Mirrors the existing grpc/json/cmux config validation in app/roles/etcd.lua. | Distinct from the watch-mirror config (tarantool-etcd-jg3): peer (2380) URLs not client (2379), advertise_peer_url required for the cluster to dial us back. | open | 2 | task | NULL | NULL | 2026-05-20T06:45:13Z | Eugene Blikh | bigbes@gmail.com | 2026-07-17T14:27:36Z | NULL | NULL | 0 | NULL | NULL | NULL | 0 | 0 | 0 | <binary> | 0 | NULL | NULL | NULL | 0 | NULL | 0 | |||||||||||||||||||||||
| tarantool-etcd-unw | f79d825d667d7493d1fbad53e65026978cea04ebcf650b8c77456f87f7698c30 | Tarantool metrics integration | Roadmap P0 blocker. Expose etcd-server operational metrics through Tarantool's metrics subsystem (request counts/latencies per RPC, watch/lease counts, revision, compaction, store size). Should surface via the standard Tarantool metrics collectors so existing TT monitoring (Prometheus exporter) picks them up. Consider parity with etcd's own /metrics names where it aids dashboards. | Roadmap-only item; no metrics issue existed in beads. | open | 0 | feature | NULL | NULL | 2026-05-20T06:55:23Z | Eugene Blikh | bigbes@gmail.com | 2026-07-17T14:27:36Z | NULL | NULL | 0 | NULL | NULL | NULL | 0 | 0 | 0 | <binary> | 0 | NULL | NULL | NULL | 0 | NULL | 0 | |||||||||||||||||||||||
| tarantool-etcd-uud | fd643145ab1fb8229578bc7680a265a12fc602153dfd6682b33c3090c46b73d7 | WatchProgressRequest not stream-synchronised; wrong response shape (per-watcher ids instead of watch_id=-1) | etcd sends ONE broadcast WatchResponse with watch_id=-1, deferred until every watcher on the stream is synced (watchable_store.go:510-539). We send one empty response PER WATCHER carrying its real watch id, emitted immediately from the recv-loop fiber while event fibers are still delivering asynchronously (app/etcd/grpc.lua:522-533 vs grpc.lua:336-427). Impact: clients keying on watch_id=-1 to recognise a progress broadcast do not see it; the 'all watchers caught up' guarantee is not provided. Race-shaped: argued from code structure, not from an observed failure — needs a reproducer first. | RequestProgress emits a single watch_id=-1 response gated on all stream watchers being caught up; reproducer test exists. | open | 2 | bug | NULL | NULL | 2026-07-17T13:20:15Z | Eugene Blikh | bigbes@gmail.com | 2026-07-17T13:20:15Z | NULL | NULL | 0 | NULL | NULL | NULL | 0 | 0 | 0 | <binary> | 0 | NULL | NULL | NULL | 0 | NULL | 0 | |||||||||||||||||||||||
| tarantool-etcd-v01 | c1e139e7df4222c2657166c19c9aa0a7382320d2dfd608fc98ced8da400afee2 | Auth never re-checked on a live Watch stream (expired token / auth revision bump ignored) | etcd surfaces ErrInvalidAuthToken / ErrAuthOldRevision on a live stream when the token expires or the auth revision changes, cancelling affected watchers. We resolve the token once at stream open and ignore failure (app/etcd/grpc.lua:263-267); READ permission is checked only at watcher create (app/etcd/watch.lua:250-252). The stream then keeps delivering with the cached identity forever. Grep: ErrAuthOldRevision has no analogue anywhere in app/. Impact: revoking a user's permission or deleting the user does not stop an already-open watch from streaming their keys. Security-relevant, hence P1 rather than P2. | Re-validate the token per event batch and cancel watchers on increment_auth_revision. Needs a way to reach live watchers from the auth module — check whether the watcher registry (watch.lua:42-44) can be indexed by identity without adding contention on the hot delivery path. | Revoking a role mid-stream cancels the watch with the etcd-shaped error; token expiry surfaces on the stream; a test pins both. | open | 1 | bug | NULL | NULL | 2026-07-17T13:20:13Z | Eugene Blikh | bigbes@gmail.com | 2026-07-17T13:20:13Z | NULL | NULL | 0 | NULL | NULL | NULL | 0 | 0 | 0 | <binary> | 0 | NULL | NULL | NULL | 0 | NULL | 0 | ||||||||||||||||||||||
| tarantool-etcd-vab | 80fa81f1f20e735f77e593b5e9975547a508502d5de68535ea41ac50091e1f7f | AuthStatus must not require prior authentication (etcd 3.7) | etcd 3.7: 'AuthStatus retrieval no longer requires prior authentication.' A client should be able to ask whether auth is enabled without already holding a token — which is the only sane ordering, since you need the answer to know whether to authenticate. Our JSON gateway gates every method except 'authenticate' behind a valid bearer token (app/etcd/json.lua:662), and the gRPC path uses the same predicate shape, so POST /v3/auth/status with no token returns 16 (Unauthenticated) once auth is enabled. Upstream 3.7 answers it. Fix: widen the exemption from a single method name to a small unauthenticated-method set containing 'authenticate' and 'status' (Auth.AuthStatus only — NOT Maintenance.Status, which routes to {'maintenance','status'} and shares the bare method name). Keep the gRPC and JSON gates driven by one predicate so they cannot drift. | Match on the (service, method) pair, not the bare method string — routes.lua has both {'auth','status'} and {'maintenance','status'}. Check upstream 3.7 whether Maintenance.Status is also unauthenticated before changing it; this issue only claims Auth.AuthStatus. | With auth enabled and no token: Auth.AuthStatus returns enabled=true + authRevision on both wires; every other method still returns Unauthenticated; a Lua test in test/auth_test.lua pins it. | open | 3 | bug | NULL | NULL | 2026-07-17T11:30:22Z | Eugene Blikh | bigbes@gmail.com | 2026-07-17T11:30:22Z | NULL | NULL | 0 | NULL | NULL | NULL | 0 | 0 | 0 | <binary> | 0 | NULL | NULL | NULL | 0 | NULL | 0 | ||||||||||||||||||||||
| tarantool-etcd-vnp | 1d9a61e1b6a987a8cb6ddae9833f7073ce29f05f3b852205dae442bfa24f3819 | Watch/Range reject compact_revision itself — off-by-one vs etcd, livelocks watch recovery | etcd keeps the tombstone whose revision == the compact revision and rejects only rev < compactMainRev (verified at server/storage/mvcc/kvstore_txn.go:80 — 'if rev < tr.s.compactMainRev'). A watch or range started exactly AT compact_revision succeeds. We reject at <= on both paths and delete the boundary row outright: - app/etcd/compact.lua:63-84 — the batch loop breaks on 'tuple.mod_revision > target_rev', so it deletes every kv_history row with mod_revision <= target_rev, INCLUDING the entry at target_rev. - app/etcd/watch.lua:281 — 'if opts.start_revision <= compacted then return errors.revision_compacted(compacted)' - app/etcd/kv.lua:261 — 'if target_rev <= compacted then error(errors.revision_compacted())' Why this is more than cosmetic: etcd's documented watch-recovery pattern is 'on a canceled response carrying compact_revision, restart the watch at compact_revision'. Against this server that revision is itself rejected, so a conforming clientv3 watcher that survives a compaction retries forever against the same rejection — an infinite cancel loop. Verified by hand (not just by the sweep agent) against both our source and the upstream clone. | Two coupled changes; do them together or the boundary stays inconsistent: (1) compaction must retain rows with mod_revision == target_rev — break the loop on '>=' rather than deleting through the boundary; (2) both guards move from '<=' to '<'. Note the retained boundary row must be the tombstone/latest version at that revision, matching etcd's key_index.go behavior of keeping the generation's last entry. | Compact(N) then Watch(start_revision=N) succeeds and delivers the event at N, matching etcd; Range(revision=N) after Compact(N) succeeds; a conformance test probes the compact-revision boundary itself on both backends (current compact_test.go only probes strictly below it); clientv3 restart-at-CompactRevision terminates. | open | 0 | bug | NULL | NULL | 2026-07-17T13:18:41Z | Eugene Blikh | bigbes@gmail.com | 2026-07-17T13:18:41Z | NULL | NULL | 0 | NULL | NULL | NULL | 0 | 0 | 0 | <binary> | 0 | NULL | NULL | NULL | 0 | NULL | 0 | ||||||||||||||||||||||
| tarantool-etcd-vru | acf76de231548d849879d27ab87990e26de3591d1efb476d9744376f2e7c1a50 | SETTINGS_MAX_CONCURRENT_STREAMS advertised as 100; etcd advertises MaxUint32 | lib/nghttp2/grpc.lua:49-54 hardcodes {MAX_CONCURRENT_STREAMS, 100} in DEFAULT_SETTINGS, submitted on every connection (grpc.lua:381-385); lib/nghttp2/server.lua:18 carries the same 100. etcd's DefaultMaxConcurrentStreams = math.MaxUint32 (verified at server/embed/config.go:68), i.e. effectively unlimited since 3.2, tunable via --max-concurrent-streams. opts.session_options (grpc.lua:373) only reaches the peer-side knob (session.lua:260-261) — it never overrides DEFAULT_SETTINGS, so there is currently no way to raise this from config. Impact: past 100 concurrent streams on one connection (in-flight unary + Watch/Lease/Lock/Election all multiplex onto a single clientv3 connection), further streams are queued or REFUSED_STREAM where etcd never throttles. Scale-dependent: harmless for small deployments, a hard ceiling for watch-heavy ones. | Raise the default to 0xFFFFFFFF and expose grpc.max_concurrent_streams in roles_cfg, wiring it through to DEFAULT_SETTINGS rather than the peer-side knob. Check whether our per-stream fiber model imposes a real resource ceiling that 100 was silently protecting — if so, the right number may not be MaxUint32, and that reasoning belongs in docs/TODO.md. | Server advertises the configured value (default effectively unlimited); >100 concurrent streams on one connection all proceed; role option documented. | open | 2 | bug | NULL | NULL | 2026-07-17T13:19:35Z | Eugene Blikh | bigbes@gmail.com | 2026-07-17T13:19:35Z | NULL | NULL | 0 | NULL | NULL | NULL | 0 | 0 | 0 | <binary> | 0 | NULL | NULL | NULL | 0 | NULL | 0 | ||||||||||||||||||||||
| tarantool-etcd-w5r | 3a127352109f5847f90475b493f71b010fa77a962ab2ab7fd469ed01e25e9984 | iproto-impl: etcd_election_campaign vs Election.Campaign | Wrap the existing handler election.campaign (already invoked by gRPC/JSON via defs.lua route v3electionpb.Election.Campaign) as a Lua global etcd_election_campaign callable via iproto CALL. Mechanical change in app/etcd/api.lua: add rawset(_G, 'etcd_election_campaign', wrap('election', 'campaign')) under the appropriate service block; append 'etcd_election_campaign' to the funcs table so box.schema.func.create plus guest grant runs at role apply; document the wrapper in the module-level LDoc comment. If Election.Campaign returns UNIMPLEMENTED today, the wrapper still gets added so the iproto caller observes the same UNIMPLEMENTED error gRPC would return. Election.Observe is server-streaming over gRPC; iproto has no stream primitive, so propose a polling shape (observe_create/_pull/_cancel) mirroring the Watch precedent at api.lua:81-83. | etcd_election_campaign is callable from net.box:call('etcd_election_campaign', {req}) with no Lua error before handler dispatch. Successful request shape produces same successful response shape as gRPC. box.schema.func.create plus guest grant idempotent on role re-apply. | Epic tarantool-etcd-w8q. Handler at app/etcd/election.lua. | open | 3 | task | NULL | NULL | 2026-05-19T14:54:43Z | Eugene Blikh | bigbes@gmail.com | 2026-07-17T14:27:36Z | NULL | NULL | 0 | NULL | NULL | NULL | 0 | 0 | 0 | <binary> | 0 | NULL | NULL | NULL | 0 | NULL | 0 | ||||||||||||||||||||||
| tarantool-etcd-w6z | a1dfa11cf641fccae10ce051a21c5eaca457020ec09cb52c85f4e5cc8d0062c1 | iproto-verify: etcd_election_campaign vs Election.Campaign | Add Lua testkit verification for etcd_election_campaign (mirrors gRPC v3electionpb.Election.Campaign). Same test pattern as verify-only tasks: positive path plus at least one error path, response shape matches gRPC, error codes from app/etcd/errors.lua. For UNIMPLEMENTED-by-design endpoints (Cluster.MemberAdd/Remove/Update/Promote), assert the wrapper returns the canonical UNIMPLEMENTED error consistent with gRPC rather than skipping. Goes in test/iproto_parity_test.lua under group iproto/parity/election. | Epic tarantool-etcd-w8q. Blocked on the matching impl issue. | open | 3 | task | NULL | NULL | 2026-05-19T14:54:44Z | Eugene Blikh | bigbes@gmail.com | 2026-07-17T14:27:37Z | NULL | NULL | 0 | NULL | NULL | NULL | 0 | 0 | 0 | <binary> | 0 | NULL | NULL | NULL | 0 | NULL | 1 | |||||||||||||||||||||||
| tarantool-etcd-w8q | 472222f8e9e50041b82f23593de29308c6f31d0649b21012f82ea3df9f38020c | iproto parity for the gRPC surface | Bring app/etcd/api.lua iproto CALL surface to functional parity with the gRPC surface in app/etcd/pb/defs.lua. 32 already-implemented endpoints need Lua verification tests; 16 missing endpoints (Cluster, Maintenance, Election) need both implementation wrappers and tests. Out of scope: Watch and LeaseKeepAlive bidi-streams, Maintenance.Snapshot/Downgrade. For UNIMPLEMENTED-by-design endpoints, wrappers are created anyway and propagate the same UNIMPLEMENTED error so the surface is uniform. | Every gRPC method in defs.lua (excluding streaming divergences) has a matching etcd_* iproto wrapper. Every wrapper has a Lua testkit test in test/iproto_parity_test.lua asserting response shape and error mapping match gRPC. test/run.lua group iproto/parity passes. | open | 2 | epic | NULL | NULL | 2026-05-19T14:53:51Z | Eugene Blikh | bigbes@gmail.com | 2026-05-19T14:53:51Z | NULL | NULL | 0 | NULL | NULL | NULL | 0 | 0 | 0 | <binary> | 0 | NULL | NULL | NULL | 0 | NULL | 0 | |||||||||||||||||||||||
| tarantool-etcd-wgx | 5a455111b3144aca15dfc7df9cce92515e6000d9cd0fa1e761c4ba493c83082f | iproto-verify: etcd_authenticate vs Auth.Authenticate | Add Lua testkit verification for etcd_authenticate (mirrors gRPC Auth.Authenticate). Successful path: assert response shape matches the gRPC response message after MsgPack round-trip. Error path: at least one error case asserting {code, message} matches app/etcd/errors.lua catalog. Exercise at least one non-default option if applicable. Goes in test/iproto_parity_test.lua under group iproto/parity/auth. Call shape: local resp = etcd_authenticate(req). | Verification-only. Wrapper exists at app/etcd/api.lua. Epic tarantool-etcd-w8q. | open | 3 | task | NULL | NULL | 2026-05-19T14:54:05Z | Eugene Blikh | bigbes@gmail.com | 2026-05-19T14:54:05Z | NULL | NULL | 0 | NULL | NULL | NULL | 0 | 0 | 0 | <binary> | 0 | NULL | NULL | NULL | 0 | NULL | 0 | |||||||||||||||||||||||
| tarantool-etcd-wqn | a7a2759a10e1b49b4c98ebf2bcc0979aff638f3764809fdee4605d8441e1adb6 | iproto-impl: etcd_maintenance_alarm vs Maintenance.Alarm | Wrap the existing handler maintenance.alarm (already invoked by gRPC/JSON via defs.lua route etcdserverpb.Maintenance.Alarm) as a Lua global etcd_maintenance_alarm callable via iproto CALL. Mechanical change in app/etcd/api.lua: add rawset(_G, 'etcd_maintenance_alarm', wrap('maintenance', 'alarm')) under the appropriate service block; append 'etcd_maintenance_alarm' to the funcs table so box.schema.func.create plus guest grant runs at role apply; document the wrapper in the module-level LDoc comment. If Maintenance.Alarm returns UNIMPLEMENTED today, the wrapper still gets added so the iproto caller observes the same UNIMPLEMENTED error gRPC would return. Election.Observe is server-streaming over gRPC; iproto has no stream primitive, so propose a polling shape (observe_create/_pull/_cancel) mirroring the Watch precedent at api.lua:81-83. | etcd_maintenance_alarm is callable from net.box:call('etcd_maintenance_alarm', {req}) with no Lua error before handler dispatch. Successful request shape produces same successful response shape as gRPC. box.schema.func.create plus guest grant idempotent on role re-apply. | Epic tarantool-etcd-w8q. Handler at app/etcd/maintenance.lua. | open | 3 | task | NULL | NULL | 2026-05-19T14:54:30Z | Eugene Blikh | bigbes@gmail.com | 2026-07-17T14:27:36Z | NULL | NULL | 0 | NULL | NULL | NULL | 0 | 0 | 0 | <binary> | 0 | NULL | NULL | NULL | 0 | NULL | 0 | ||||||||||||||||||||||
| tarantool-etcd-ymm | 5abccb539c482913cabd242acf2b32afd6eb182fda57781a502ac4411d704a0d | iproto-impl: etcd_maintenance_move_leader vs Maintenance.MoveLeader | Wrap the existing handler maintenance.move_leader (already invoked by gRPC/JSON via defs.lua route etcdserverpb.Maintenance.MoveLeader) as a Lua global etcd_maintenance_move_leader callable via iproto CALL. Mechanical change in app/etcd/api.lua: add rawset(_G, 'etcd_maintenance_move_leader', wrap('maintenance', 'move_leader')) under the appropriate service block; append 'etcd_maintenance_move_leader' to the funcs table so box.schema.func.create plus guest grant runs at role apply; document the wrapper in the module-level LDoc comment. If Maintenance.MoveLeader returns UNIMPLEMENTED today, the wrapper still gets added so the iproto caller observes the same UNIMPLEMENTED error gRPC would return. Election.Observe is server-streaming over gRPC; iproto has no stream primitive, so propose a polling shape (observe_create/_pull/_cancel) mirroring the Watch precedent at api.lua:81-83. | etcd_maintenance_move_leader is callable from net.box:call('etcd_maintenance_move_leader', {req}) with no Lua error before handler dispatch. Successful request shape produces same successful response shape as gRPC. box.schema.func.create plus guest grant idempotent on role re-apply. | Epic tarantool-etcd-w8q. Handler at app/etcd/maintenance.lua. | open | 3 | task | NULL | NULL | 2026-05-19T14:54:41Z | Eugene Blikh | bigbes@gmail.com | 2026-07-17T14:27:36Z | NULL | NULL | 0 | NULL | NULL | NULL | 0 | 0 | 0 | <binary> | 0 | NULL | NULL | NULL | 0 | NULL | 0 | ||||||||||||||||||||||
| tarantool-etcd-zc7 | 47699b40eacc700cd65c09da563573fbd2ac63d67fd9ba709b6178394119ad21 | CLI utility: migrate existing config/data into tarantool-etcd | Provide a configuration migration utility that moves an existing etcd configuration (and KV data) into a tarantool-etcd store, so users can adopt tarantool-etcd without hand-copying keys. Should cover the Tarantool EE centralized-config flow demonstrated in examples/tarantool-ee-self-hosted-etcd and examples/tarantool-ee-config: read config from a source (local cluster-config.yaml or a real etcd endpoint) and write it under the configured prefix into tarantool-etcd over the /v3 wire. Scope to nail down: source modes (file vs live etcd), key-range copy semantics, prefix handling, dry-run/diff output, and idempotency on re-run. | open | 2 | feature | NULL | NULL | 2026-05-20T14:33:31Z | Eugene Blikh | bigbes@gmail.com | 2026-05-20T14:33:31Z | NULL | NULL | 0 | NULL | NULL | NULL | 0 | 0 | 0 | <binary> | 0 | NULL | NULL | NULL | 0 | NULL | 0 | ||||||||||||||||||||||||
| tarantool-etcd-zkg | 72a34c0ab42f90eede7cbbfd3ec5f7ef4f98cdbb5232099ad2926aaa87a61111 | Learner: conformance — join real etcd cluster, assert log replication | End-to-end test in tests/conformance: SpawnEtcd (or a small etcd cluster), MemberAddAsLearner the tarantool node with its advertise_peer_url, wait for it to become a healthy learner, then: (1) Put on the real etcd leader, assert it appears via Range on the tarantool learner with the SAME mod_revision; (2) assert MemberList on the cluster shows the tarantool node as IsLearner=true; (3) lease grant + key-with-lease on the leader replicates and the learner reflects lease attachment; (4) auth enable on the leader propagates; (5) force a snapshot (lower --snapshot-count, churn keys past compaction) and assert the learner recovers via snapshot receive. Use the harness, never tt start (memory feedback_use_harness_not_tt). | This is the correctness oracle for the whole epic — each phase above is unverifiable in isolation against real etcd without it. Gate behind a build tag / env since it boots a foreign etcd cluster. | open | 2 | task | NULL | NULL | 2026-05-20T06:45:18Z | Eugene Blikh | bigbes@gmail.com | 2026-07-17T14:27:37Z | NULL | NULL | 0 | NULL | NULL | NULL | 0 | 0 | 0 | <binary> | 0 | NULL | NULL | NULL | 0 | NULL | 1 | |||||||||||||||||||||||
| tarantool-etcd-zl3 | a8b07f282a651d33df22d0c4f016408f1864fdcea3d059bb2d8b617e9585f9d3 | Maintenance RPCs not root-gated — and docs/TODO.md justifies it with a false claim about etcd 3.6 | TWO problems; the doc one is confirmed and independently verified, the code one is a real decision to make. 1) DOC BUG (verified by hand at the v3.6.0 tag). docs/TODO.md:130-132 states: 'Maintenance RPCs are not RBAC-gated — matches etcd 3.6, which doesn't permission-check Status/Hash/HashKV/Defragment/Alarm/MoveLeader.' That is factually wrong for 5 of the 6. At v3.6.0, NewMaintenanceServer returns &authMaintenanceServer{srv, &AuthAdmin{s}} — always wrapped — and Defragment/Snapshot/Hash/HashKV/Status/MoveLeader/Downgrade each call ams.isPermitted(ctx). Only Alarm is genuinely ungated, matching us. A deliberate divergence resting on a false premise is not a deliberate divergence; fix the entry regardless of what we decide to implement. 2) CODE GAP. Zero require_admin/require_permission in app/etcd/maintenance.lua and cluster.lua; auth.begin_grpc_request passes anonymous through (auth.lua:356-358). So any unauthenticated client can call Status/Hash/HashKV/Defragment/MoveLeader with auth enabled. Scope correction from the sweep's refuter: Cluster.MemberList non-gating DOES match v3.6.0; the requireAuthInfo relaxations are 3.7-dev only. Real scope is the 5 maintenance RPCs (Snapshot/Downgrade are unimplemented here anyway). | docs/TODO.md:130-132 corrected to state what etcd 3.6.0 actually does; the 5 RPCs root-gated (or a corrected, true rationale recorded for keeping them open); Alarm stays ungated. | open | 2 | bug | NULL | NULL | 2026-07-17T13:20:14Z | Eugene Blikh | bigbes@gmail.com | 2026-07-17T13:20:14Z | NULL | NULL | 0 | NULL | NULL | NULL | 0 | 0 | 0 | <binary> | 0 | NULL | NULL | NULL | 0 | NULL | 0 | |||||||||||||||||||||||
| tarantool-etcd-zwg | 5f1f8ad7dadfa6be0ecaa0dc1d71f690076e930bc5073feb538b71faf1f2d7d6 | iproto-verify: etcd_user_get vs Auth.UserGet | Add Lua testkit verification for etcd_user_get (mirrors gRPC Auth.UserGet). Successful path: assert response shape matches the gRPC response message after MsgPack round-trip. Error path: at least one error case asserting {code, message} matches app/etcd/errors.lua catalog. Exercise at least one non-default option if applicable. Goes in test/iproto_parity_test.lua under group iproto/parity/auth. Call shape: local resp = etcd_user_get(req). | Verification-only. Wrapper exists at app/etcd/api.lua. Epic tarantool-etcd-w8q. | open | 3 | task | NULL | NULL | 2026-05-19T14:54:08Z | Eugene Blikh | bigbes@gmail.com | 2026-05-19T14:54:08Z | NULL | NULL | 0 | NULL | NULL | NULL | 0 | 0 | 0 | <binary> | 0 | NULL | NULL | NULL | 0 | NULL | 0 | |||||||||||||||||||||||
| tarantool-etcd-zws | b99aec7b0025f26f4e59b5bf3e6287ee00f117d8f119775c13dcfbb23132d00e | Replica route: revision alignment with upstream | In replica mode the local global revision sequence (box.sequence in revision.lua) must mirror upstream's revisions exactly so that Range/Watch reads return etcd-consistent create_revision/mod_revision/header.revision. Work: - While replica mode is active, the local revision allocator must NOT mint its own revisions for replicated writes; it must set revision = the upstream mod_revision carried by each Range/Watch event. - Decide handling of the box.sequence: drive it to upstream's current revision (set, not next()) as events apply, or bypass it entirely for replicated mutations via the *_internal write paths. - Ensure header.revision returned to local clients equals the latest synced upstream revision, and compact_revision tracks upstream compaction. This is cross-cutting with bootstrap + watch-tail; it defines the invariant both must uphold. | revision.lua owns the box.sequence. Replicated writes likely go through put_internal/delete_range_internal with an explicit revision arg to avoid double-allocation. | open | 2 | task | NULL | NULL | 2026-05-20T06:17:55Z | Eugene Blikh | bigbes@gmail.com | 2026-07-17T14:27:37Z | NULL | NULL | 0 | NULL | NULL | NULL | 0 | 0 | 0 | <binary> | 0 | NULL | NULL | NULL | 0 | NULL | 1 |