diff --git a/changes/ticket20895 b/changes/ticket20895
new file mode 100644
index 0000000000000000000000000000000000000000..a1d82049978e8fa685e2d2bc76b834a311368a77
--- /dev/null
+++ b/changes/ticket20895
@@ -0,0 +1,6 @@
+  o Minor features (forward-compatibility):
+    - If a relay supports some link authentication protocol that we do not
+      recognize, then include that relay's ed25519 key when telling other
+      relays to extend to it.  Previously, we treated future versions as if
+      they were too old to support ed25519 link authentication.
+      Closes ticket 20895.
diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c
index 279308afcbf9db1e26978bba73981e20a6b1b2a4..45bf0195450074009c50aa0cd71edd9126988d90 100644
--- a/src/or/circuitbuild.c
+++ b/src/or/circuitbuild.c
@@ -1290,7 +1290,7 @@ circuit_extend(cell_t *cell, circuit_t *circ)
     const node_t *node = node_get_by_id((const char*)ec.node_id);
     const ed25519_public_key_t *node_ed_id = NULL;
     if (node &&
-        node_supports_ed25519_link_authentication(node) &&
+        node_supports_ed25519_link_authentication(node, 1) &&
         (node_ed_id = node_get_ed25519_id(node))) {
       ed25519_pubkey_copy(&ec.ed_pubkey, node_ed_id);
     }
@@ -2698,7 +2698,7 @@ extend_info_from_node(const node_t *node, int for_direct_connect)
 
   /* Don't send the ed25519 pubkey unless the target node actually supports
    * authenticating with it. */
-  if (node_supports_ed25519_link_authentication(node)) {
+  if (node_supports_ed25519_link_authentication(node, 0)) {
     log_info(LD_CIRC, "Including Ed25519 ID for %s", node_describe(node));
     ed_pubkey = node_get_ed25519_id(node);
   } else if (node_get_ed25519_id(node)) {
diff --git a/src/or/connection_or.c b/src/or/connection_or.c
index d890b58da6fe04867f27ce43e3b8ba22ed2a5964..29c1166b3c58c66bd5021de3597997cb8f55835b 100644
--- a/src/or/connection_or.c
+++ b/src/or/connection_or.c
@@ -886,7 +886,7 @@ connection_or_check_canonicity(or_connection_t *conn, int started_here)
 
   const node_t *r = node_get_by_id(id_digest);
   if (r &&
-      node_supports_ed25519_link_authentication(r) &&
+      node_supports_ed25519_link_authentication(r, 1) &&
       ! node_ed25519_id_matches(r, ed_id)) {
     /* If this node is capable of proving an ed25519 ID,
      * we can't call this a canonical connection unless both IDs match. */
diff --git a/src/or/dirserv.c b/src/or/dirserv.c
index 06ac15d587c5392efbd439cf84fe8488b65ac862..75cd0879aa2104677c74309e9f5f0f3defda8966 100644
--- a/src/or/dirserv.c
+++ b/src/or/dirserv.c
@@ -3284,7 +3284,7 @@ dirserv_orconn_tls_done(const tor_addr_t *addr,
   ri = node->ri;
 
   if (get_options()->AuthDirTestEd25519LinkKeys &&
-      node_supports_ed25519_link_authentication(node) &&
+      node_supports_ed25519_link_authentication(node, 1) &&
       ri->cache_info.signing_key_cert) {
     /* We allow the node to have an ed25519 key if we haven't been told one in
      * the routerinfo, but if we *HAVE* been told one in the routerinfo, it
@@ -3367,7 +3367,7 @@ dirserv_single_reachability_test(time_t now, routerinfo_t *router)
   tor_assert(node);
 
   if (options->AuthDirTestEd25519LinkKeys &&
-      node_supports_ed25519_link_authentication(node)) {
+      node_supports_ed25519_link_authentication(node, 1)) {
     ed_id_key = &router->cache_info.signing_key_cert->signing_key;
   } else {
     ed_id_key = NULL;
diff --git a/src/or/hs_service.c b/src/or/hs_service.c
index 5759aa81273157bd97cc33d434bd5fd4eacc1c54..5ae7614e2963ac24e0d3400bbb9cf86fdff1dfc4 100644
--- a/src/or/hs_service.c
+++ b/src/or/hs_service.c
@@ -1494,7 +1494,7 @@ pick_intro_point(unsigned int direct_conn, smartlist_t *exclude_nodes)
   /* Let's do a basic sanity check here so that we don't end up advertising the
    * ed25519 identity key of relays that don't actually support the link
    * protocol */
-  if (!node_supports_ed25519_link_authentication(node)) {
+  if (!node_supports_ed25519_link_authentication(node, 0)) {
     tor_assert_nonfatal(ed25519_public_key_is_zero(&info->ed_identity));
   }
 
diff --git a/src/or/nodelist.c b/src/or/nodelist.c
index 00b8fb144fadbf68b3a20c41a3fff0620229364c..1f2d37f285f4773edf493e96c5566f8259350bd4 100644
--- a/src/or/nodelist.c
+++ b/src/or/nodelist.c
@@ -962,23 +962,29 @@ node_ed25519_id_matches(const node_t *node, const ed25519_public_key_t *id)
 }
 
 /** Return true iff <b>node</b> supports authenticating itself
- * by ed25519 ID during the link handshake in a way that we can understand
- * when we probe it. */
+ * by ed25519 ID during the link handshake.  If <b>compatible_with_us</b>,
+ * it needs to be using a link authentication method that we understand.
+ * If not, any plausible link authentication method will do. */
 int
-node_supports_ed25519_link_authentication(const node_t *node)
+node_supports_ed25519_link_authentication(const node_t *node,
+                                          int compatible_with_us)
 {
-  /* XXXX Oh hm. What if some day in the future there are link handshake
-   * versions that aren't 3 but which are ed25519 */
   if (! node_get_ed25519_id(node))
     return 0;
   if (node->ri) {
     const char *protos = node->ri->protocol_list;
     if (protos == NULL)
       return 0;
-    return protocol_list_supports_protocol(protos, PRT_LINKAUTH, 3);
+    if (compatible_with_us)
+      return protocol_list_supports_protocol(protos, PRT_LINKAUTH, 3);
+    else
+      return protocol_list_supports_protocol_or_later(protos, PRT_LINKAUTH, 3);
   }
   if (node->rs) {
-    return node->rs->supports_ed25519_link_handshake;
+    if (compatible_with_us)
+      return node->rs->supports_ed25519_link_handshake_compat;
+    else
+      return node->rs->supports_ed25519_link_handshake_any;
   }
   tor_assert_nonfatal_unreached_once();
   return 0;
diff --git a/src/or/nodelist.h b/src/or/nodelist.h
index 427e449ad92a1ce81925e916c41da5fa3430d7d3..58743c5e78d7d4911d8a7bc93fe5b0afd30aaf80 100644
--- a/src/or/nodelist.h
+++ b/src/or/nodelist.h
@@ -65,7 +65,8 @@ const smartlist_t *node_get_declared_family(const node_t *node);
 const ed25519_public_key_t *node_get_ed25519_id(const node_t *node);
 int node_ed25519_id_matches(const node_t *node,
                             const ed25519_public_key_t *id);
-int node_supports_ed25519_link_authentication(const node_t *node);
+int node_supports_ed25519_link_authentication(const node_t *node,
+                                              int compatible_with_us);
 int node_supports_v3_hsdir(const node_t *node);
 int node_supports_ed25519_hs_intro(const node_t *node);
 int node_supports_v3_rendezvous_point(const node_t *node);
diff --git a/src/or/or.h b/src/or/or.h
index 161d80ed96ca5d099fe030db6e1d940e6be1ff99..8e1d15b8cbab711ca1d042731dc6414983a7b317 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -2316,8 +2316,12 @@ typedef struct routerstatus_t {
   unsigned int supports_extend2_cells:1;
 
   /** True iff this router has a protocol list that allows it to negotiate
-   * ed25519 identity keys on a link handshake. */
-  unsigned int supports_ed25519_link_handshake:1;
+   * ed25519 identity keys on a link handshake with us. */
+  unsigned int supports_ed25519_link_handshake_compat:1;
+
+  /** True iff this router has a protocol list that allows it to negotiate
+   * ed25519 identity keys on a link handshake, at all. */
+  unsigned int supports_ed25519_link_handshake_any:1;
 
   /** True iff this router has a protocol list that allows it to be an
    * introduction point supporting ed25519 authentication key which is part of
diff --git a/src/or/routerparse.c b/src/or/routerparse.c
index 08c3fc0a026b263bca69cc16f4c3d2d384c51389..b26231db01a401fd170307325029d40cb39efd74 100644
--- a/src/or/routerparse.c
+++ b/src/or/routerparse.c
@@ -2701,8 +2701,10 @@ routerstatus_parse_entry_from_string(memarea_t *area,
     rs->protocols_known = 1;
     rs->supports_extend2_cells =
       protocol_list_supports_protocol(tok->args[0], PRT_RELAY, 2);
-    rs->supports_ed25519_link_handshake =
+    rs->supports_ed25519_link_handshake_compat =
       protocol_list_supports_protocol(tok->args[0], PRT_LINKAUTH, 3);
+    rs->supports_ed25519_link_handshake_any =
+      protocol_list_supports_protocol_or_later(tok->args[0], PRT_LINKAUTH, 3);
     rs->supports_ed25519_hs_intro =
       protocol_list_supports_protocol(tok->args[0], PRT_HSINTRO, 4);
     rs->supports_v3_hsdir =