diff --git a/src/feature/relay/router.c b/src/feature/relay/router.c
index ab5fe697bca71afd70511a82876a730d8333a386..cc317dfad3c9e3923b760f7064929710287b54b9 100644
--- a/src/feature/relay/router.c
+++ b/src/feature/relay/router.c
@@ -3034,6 +3034,34 @@ router_dump_router_to_string(routerinfo_t *router,
     we_are_hibernating() ? "hibernating 1\n" : "",
     "hidden-service-dir\n");
 
+  SMARTLIST_FOREACH_BEGIN(get_current_family_id_keys(),
+                          const ed25519_keypair_t *, k_family_id) {
+    // TODO PROP321: We may want this to be configurable;
+    // we can probably use a smaller value.
+#define FAMILY_CERT_LIFETIME (30*86400)
+    tor_cert_t *family_cert = tor_cert_create_ed25519(
+          k_family_id,
+          CERT_TYPE_FAMILY_V_IDENTITY,
+          get_master_identity_key(),
+          router->cache_info.published_on,
+          FAMILY_CERT_LIFETIME, CERT_FLAG_INCLUDE_SIGNING_KEY);
+    char family_cert_base64[256];
+    if (base64_encode(family_cert_base64, sizeof(family_cert_base64),
+                      (const char*) family_cert->encoded,
+                      family_cert->encoded_len, BASE64_ENCODE_MULTILINE) < 0) {
+      log_err(LD_BUG, "Base64 encoding family cert failed!?");
+      tor_cert_free(family_cert);
+      goto err;
+    }
+    smartlist_add_asprintf(chunks,
+                           "family-cert\n"
+                           "-----BEGIN FAMILY CERT-----\n"
+                           "%s"
+                           "-----END FAMILY CERT-----\n",
+                           family_cert_base64);
+    tor_cert_free(family_cert);
+  } SMARTLIST_FOREACH_END(k_family_id);
+
   if (options->ContactInfo && strlen(options->ContactInfo)) {
     const char *ci = options->ContactInfo;
     if (strchr(ci, '\n') || strchr(ci, '\r'))
diff --git a/src/feature/relay/routerkeys.c b/src/feature/relay/routerkeys.c
index 64ec38ed19cfa6409fc5e1848f93c81d108da339..5bfc2ebe65a090710367757b37da34f2e2c84cee 100644
--- a/src/feature/relay/routerkeys.c
+++ b/src/feature/relay/routerkeys.c
@@ -44,6 +44,9 @@ static uint8_t *rsa_ed_crosscert = NULL;
 static size_t rsa_ed_crosscert_len = 0;
 static time_t rsa_ed_crosscert_expiration = 0;
 
+// list of ed25519_keypair_t
+static smartlist_t *family_id_keys = NULL;
+
 /**
  * Running as a server: load, reload, or refresh our ed25519 keys and
  * certificates, creating and saving new ones as needed.
@@ -674,6 +677,43 @@ get_current_auth_key_cert(void)
   return auth_key_cert;
 }
 
+/**
+ * Return a list of our current family id keypairs,
+ * as a list of `ed25519_keypair_t`.
+ *
+ * Never returns NULL.
+ *
+ * TODO PROP321: Right now this is only used in testing;
+ * when we add relay support we'll need a way to actually
+ * read these keys from disk.
+ **/
+const smartlist_t *
+get_current_family_id_keys(void)
+{
+  if (family_id_keys == NULL)
+    family_id_keys = smartlist_new();
+  return family_id_keys;
+}
+
+#ifdef TOR_UNIT_TESTS
+/**
+ * Testing only: Replace our list of family ID keys with `family_id_keys`,
+ * which must be a list of `ed25519_keypair_t`.
+ *
+ * Takes ownership of its input.
+ */
+void
+set_mock_family_id_keys(smartlist_t *keys)
+{
+  if (family_id_keys) {
+    SMARTLIST_FOREACH(family_id_keys, ed25519_keypair_t *, kp,
+                      ed25519_keypair_free(kp));
+    smartlist_free(family_id_keys);
+  }
+  family_id_keys = keys;
+}
+#endif
+
 void
 get_master_rsa_crosscert(const uint8_t **cert_out,
                          size_t *size_out)
@@ -746,6 +786,12 @@ routerkeys_free_all(void)
   ed25519_keypair_free(master_identity_key);
   ed25519_keypair_free(master_signing_key);
   ed25519_keypair_free(current_auth_key);
+  if (family_id_keys) {
+    SMARTLIST_FOREACH(family_id_keys, ed25519_keypair_t *, kp,
+                      ed25519_keypair_free(kp));
+    smartlist_free(family_id_keys);
+  }
+
   tor_cert_free(signing_key_cert);
   tor_cert_free(link_cert_cert);
   tor_cert_free(auth_key_cert);
diff --git a/src/feature/relay/routerkeys.h b/src/feature/relay/routerkeys.h
index b97615a9c9022e18e395c910e9487156edab6fa5..f51f91b141311fa28f3646016e245ab1f6b99766 100644
--- a/src/feature/relay/routerkeys.h
+++ b/src/feature/relay/routerkeys.h
@@ -21,6 +21,8 @@ const ed25519_keypair_t *get_current_auth_keypair(void);
 const struct tor_cert_st *get_current_link_cert_cert(void);
 const struct tor_cert_st *get_current_auth_key_cert(void);
 
+const smartlist_t *get_current_family_id_keys(void);
+
 void get_master_rsa_crosscert(const uint8_t **cert_out,
                               size_t *size_out);
 
@@ -126,6 +128,7 @@ make_tap_onion_key_crosscert(const crypto_pk_t *onion_key,
 #ifdef TOR_UNIT_TESTS
 const ed25519_keypair_t *get_master_identity_keypair(void);
 void init_mock_ed_keys(const crypto_pk_t *rsa_identity_key);
+void set_mock_family_id_keys(smartlist_t *keys);
 #endif
 
 #endif /* !defined(TOR_ROUTERKEYS_H) */