diff --git a/app/lib/constants.ts b/app/lib/constants.ts
index 7092d69e5ff3ccb15b66161198ad571936edd2ac..ee55efaa0229b188d87e50d1018478c667655d12 100644
--- a/app/lib/constants.ts
+++ b/app/lib/constants.ts
@@ -146,6 +146,7 @@ module.exports = {
       STEPMAX: 3,
       SIGDELAY: 3600 * 24 * 365 * 5,
       SIGPERIOD: 0, // Instant
+      SIGREPLAY: 0, // Instant
       MSPERIOD: 0, // Instant
       SIGSTOCK: 40,
       SIGWINDOW: 3600 * 24 * 7, // a week
diff --git a/app/lib/dal/fileDAL.ts b/app/lib/dal/fileDAL.ts
index be9960bb138709210277ea918c215fbcd94cc7b8..36603d84f974792f3a93c3ecefb3adb363b7f26e 100644
--- a/app/lib/dal/fileDAL.ts
+++ b/app/lib/dal/fileDAL.ts
@@ -396,6 +396,11 @@ export class FileDAL {
     return this.blockDAL.getCountOfBlocksIssuedBy(issuer)
   }
 
+  /**
+   * Find all the blocks in the blockchain whose number is between [start ; end]
+   * @param start Lower number bound (included).
+   * @param end Higher number bound (included).
+   */
   async getBlocksBetween (start:number, end:number) {
     start = Math.max(0, start)
     end= Math.max(0, end)
@@ -878,8 +883,8 @@ export class FileDAL {
       .value();
   }
 
-  existsNonReplayableLink(from:string, to:string) {
-    return  this.cindexDAL.existsNonReplayableLink(from, to)
+  existsNonReplayableLink(from:string, to:string, medianTime: number, version: number) {
+    return  this.cindexDAL.existsNonReplayableLink(from, to, medianTime, version)
   }
 
   async getSource(identifier:string, pos:number, isDividend: boolean): Promise<SimpleTxInput | null> {
@@ -919,10 +924,13 @@ export class FileDAL {
     return (ms && ms.leaving) || false;
   }
 
-  async existsCert(cert:any) {
+  async existsCert(cert: DBCert, current: DBBlock|null) {
     const existing = await this.certDAL.existsGivenCert(cert);
     if (existing) return existing;
-    const existsLink = await this.cindexDAL.existsNonReplayableLink(cert.from, cert.to);
+    if (!current) {
+      return false
+    }
+    const existsLink = await this.cindexDAL.existsNonReplayableLink(cert.from, cert.to, current.medianTime, current.version)
     return !!existsLink;
   }
 
diff --git a/app/lib/dal/indexDAL/abstract/CIndexDAO.ts b/app/lib/dal/indexDAL/abstract/CIndexDAO.ts
index 3f1cefa2b9889e67ebcd2d3e7c15574e73b7166d..2998c6b41f7b43cc8b9cb87235f45390c6206c23 100644
--- a/app/lib/dal/indexDAL/abstract/CIndexDAO.ts
+++ b/app/lib/dal/indexDAL/abstract/CIndexDAO.ts
@@ -15,7 +15,7 @@ export interface CIndexDAO extends ReduceableDAO<CindexEntry> {
 
   findByReceiverAndExpiredOn(pub: string, expired_on: number): Promise<CindexEntry[]>
 
-  existsNonReplayableLink(issuer:string, receiver:string): Promise<boolean>
+  existsNonReplayableLink(issuer:string, receiver:string, medianTime: number, version: number): Promise<boolean>
 
   getReceiversAbove(minsig: number): Promise<string[]>
 
diff --git a/app/lib/dal/indexDAL/leveldb/LevelDBCindex.ts b/app/lib/dal/indexDAL/leveldb/LevelDBCindex.ts
index d8dfd0ada7b98bdc01538ffac6a733c37816fa59..972f4181f3bf61359a8e23aa903a65ad555bc6ff 100644
--- a/app/lib/dal/indexDAL/leveldb/LevelDBCindex.ts
+++ b/app/lib/dal/indexDAL/leveldb/LevelDBCindex.ts
@@ -88,7 +88,7 @@ export class LevelDBCindex extends LevelDBTable<LevelDBCindexEntry> implements C
     issuers = Underscore.uniq(issuers)
     await Promise.all(issuers.map(async issuer => {
       const entry = await this.get(issuer)
-      const fullEntries = reduceBy(entry.issued, ['issuer', 'receiver', 'created_on'])
+      const fullEntries = reduceBy(entry.issued, ['issuer', 'receiver'])
       const toRemove: string[] = []
       // We remember the maximum value of expired_on, for efficient trimming  search
       fullEntries
@@ -190,10 +190,10 @@ export class LevelDBCindex extends LevelDBTable<LevelDBCindexEntry> implements C
     }
   }
 
-  async existsNonReplayableLink(issuer: string, receiver: string): Promise<boolean> {
+  async existsNonReplayableLink(issuer: string, receiver: string, medianTime: number, version: number): Promise<boolean> {
     const entries = await this.findByIssuer(issuer)
-    const reduced = Indexer.DUP_HELPERS.reduceBy(entries, ['issuer', 'receiver', 'created_on'])
-    return reduced.filter(e => e.receiver === receiver && !e.expired_on).length > 0
+    const reduced = Indexer.DUP_HELPERS.reduceBy(entries, ['issuer', 'receiver'])
+    return reduced.filter(e => e.receiver === receiver && (version <= 10 || e.replayable_on >= medianTime)).length > 0
   }
 
   async findByIssuer(issuer: string): Promise<CindexEntry[]> {
@@ -219,7 +219,7 @@ export class LevelDBCindex extends LevelDBTable<LevelDBCindexEntry> implements C
   async findExpiresOnLteNotExpiredYet(medianTime: number): Promise<CindexEntry[]> {
     const issuers: string[] = Underscore.uniq((await this.indexForExpiresOn.findAllValues({ lte: LevelDBCindex.trimExpiredOnKey(medianTime) })).reduce(reduceConcat, []))
     return (await Promise.all(issuers.map(async issuer => {
-      const fullEntries = Indexer.DUP_HELPERS.reduceBy((await this.get(issuer)).issued, ['issuer', 'receiver', 'created_on'])
+      const fullEntries = Indexer.DUP_HELPERS.reduceBy((await this.get(issuer)).issued, ['issuer', 'receiver'])
       return fullEntries.filter(e => e.expires_on <= medianTime && !e.expired_on)
     }))).reduce(reduceConcat, [])
   }
@@ -229,21 +229,21 @@ export class LevelDBCindex extends LevelDBTable<LevelDBCindexEntry> implements C
   }
 
   async getValidLinksFrom(issuer: string): Promise<CindexEntry[]> {
-    const fullEntries = Indexer.DUP_HELPERS.reduceBy(((await this.getOrNull(issuer)) || { issued: [] }).issued, ['issuer', 'receiver', 'created_on'])
+    const fullEntries = Indexer.DUP_HELPERS.reduceBy(((await this.getOrNull(issuer)) || { issued: [] }).issued, ['issuer', 'receiver'])
     return fullEntries.filter(e => !e.expired_on)
   }
 
   async getValidLinksTo(receiver: string): Promise<CindexEntry[]> {
     const issuers: string[] = ((await this.getOrNull(receiver)) || { issued: [], received: [] }).received
     return (await Promise.all(issuers.map(async issuer => {
-      const fullEntries = Indexer.DUP_HELPERS.reduceBy((await this.get(issuer)).issued, ['issuer', 'receiver', 'created_on'])
+      const fullEntries = Indexer.DUP_HELPERS.reduceBy((await this.get(issuer)).issued, ['issuer', 'receiver'])
       return fullEntries.filter(e => e.receiver === receiver && !e.expired_on)
     }))).reduce(reduceConcat, [])
   }
 
   async reducablesFrom(from: string): Promise<FullCindexEntry[]> {
     const entries = ((await this.getOrNull(from)) || { issued: [], received: [] }).issued
-    return Indexer.DUP_HELPERS.reduceBy(entries, ['issuer', 'receiver', 'created_on'])
+    return Indexer.DUP_HELPERS.reduceBy(entries, ['issuer', 'receiver'])
   }
 
   trimExpiredCerts(belowNumber: number): Promise<void> {
diff --git a/app/lib/dto/BlockDTO.ts b/app/lib/dto/BlockDTO.ts
index 9ff80b246689d8e6f9211ccb565bbf8164c95b43..4a25a8f4fa0210ce705103d546dd09100ae6e2a4 100644
--- a/app/lib/dto/BlockDTO.ts
+++ b/app/lib/dto/BlockDTO.ts
@@ -280,7 +280,8 @@ export class BlockDTO implements Cloneable {
       udReevalTime0: parseInt(sp[18]),
       dtReeval: parseInt(sp[19]),
       // New parameter, defaults to msWindow
-      msPeriod: parseInt(sp[9])
+      msPeriod: parseInt(sp[9]),
+      sigReplay: parseInt(sp[9]),
     }
   }
 
diff --git a/app/lib/dto/ConfDTO.ts b/app/lib/dto/ConfDTO.ts
index b17a17ee6ca1e8dd4ea2ed05b64637beb0f3e3b7..3b8ed67ab22d6616b011df6093b4cd6f99e07411 100644
--- a/app/lib/dto/ConfDTO.ts
+++ b/app/lib/dto/ConfDTO.ts
@@ -44,6 +44,7 @@ export interface CurrencyConfDTO {
   dt: number
   ud0: number
   sigPeriod: number
+  sigReplay: number
   sigStock: number
   sigWindow: number
   sigValidity: number
@@ -132,6 +133,7 @@ export class ConfDTO implements StorageDTO, CurrencyConfDTO, KeypairConfDTO, Net
     public udReevalTime0: number,
     public stepMax: number,
     public sigPeriod: number,
+    public sigReplay: number,
     public msPeriod: number,
     public sigValidity: number,
     public msValidity: number,
@@ -195,7 +197,7 @@ export class ConfDTO implements StorageDTO, CurrencyConfDTO, KeypairConfDTO, Net
 ) {}
 
   static mock() {
-    return new ConfDTO("", "", [], [], 0, 3600 * 1000, constants.PROOF_OF_WORK.DEFAULT.CPU, 1, constants.PROOF_OF_WORK.DEFAULT.PREFIX, 0, 0, constants.CONTRACT.DEFAULT.C, constants.CONTRACT.DEFAULT.DT, constants.CONTRACT.DEFAULT.DT_REEVAL, 0, constants.CONTRACT.DEFAULT.UD0, 0, 0, constants.CONTRACT.DEFAULT.STEPMAX, constants.CONTRACT.DEFAULT.SIGPERIOD, 0, constants.CONTRACT.DEFAULT.SIGVALIDITY, constants.CONTRACT.DEFAULT.MSVALIDITY, constants.CONTRACT.DEFAULT.SIGQTY, constants.CONTRACT.DEFAULT.SIGSTOCK, constants.CONTRACT.DEFAULT.X_PERCENT, constants.CONTRACT.DEFAULT.PERCENTROT, constants.CONTRACT.DEFAULT.POWDELAY, constants.CONTRACT.DEFAULT.AVGGENTIME, constants.CONTRACT.DEFAULT.MEDIANTIMEBLOCKS, false, 3000, false, constants.BRANCHES.DEFAULT_WINDOW_SIZE, constants.CONTRACT.DEFAULT.IDTYWINDOW, constants.CONTRACT.DEFAULT.MSWINDOW, constants.CONTRACT.DEFAULT.SIGWINDOW, 0, { pub:'', sec:'' }, null, "", "", 0, "", "", "", "", 0, "", "", null, false, "", true, true, false, 100, new ProxiesConf(), undefined)
+    return new ConfDTO("", "", [], [], 0, 3600 * 1000, constants.PROOF_OF_WORK.DEFAULT.CPU, 1, constants.PROOF_OF_WORK.DEFAULT.PREFIX, 0, 0, constants.CONTRACT.DEFAULT.C, constants.CONTRACT.DEFAULT.DT, constants.CONTRACT.DEFAULT.DT_REEVAL, 0, constants.CONTRACT.DEFAULT.UD0, 0, 0, constants.CONTRACT.DEFAULT.STEPMAX, constants.CONTRACT.DEFAULT.SIGPERIOD, constants.CONTRACT.DEFAULT.SIGREPLAY, 0, constants.CONTRACT.DEFAULT.SIGVALIDITY, constants.CONTRACT.DEFAULT.MSVALIDITY, constants.CONTRACT.DEFAULT.SIGQTY, constants.CONTRACT.DEFAULT.SIGSTOCK, constants.CONTRACT.DEFAULT.X_PERCENT, constants.CONTRACT.DEFAULT.PERCENTROT, constants.CONTRACT.DEFAULT.POWDELAY, constants.CONTRACT.DEFAULT.AVGGENTIME, constants.CONTRACT.DEFAULT.MEDIANTIMEBLOCKS, false, 3000, false, constants.BRANCHES.DEFAULT_WINDOW_SIZE, constants.CONTRACT.DEFAULT.IDTYWINDOW, constants.CONTRACT.DEFAULT.MSWINDOW, constants.CONTRACT.DEFAULT.SIGWINDOW, 0, { pub:'', sec:'' }, null, "", "", 0, "", "", "", "", 0, "", "", null, false, "", true, true, false, 100, new ProxiesConf(), undefined)
   }
 
   static defaultConf() {
@@ -211,6 +213,7 @@ export class ConfDTO implements StorageDTO, CurrencyConfDTO, KeypairConfDTO, Net
       "ud0": constants.CONTRACT.DEFAULT.UD0,
       "stepMax": constants.CONTRACT.DEFAULT.STEPMAX,
       "sigPeriod": constants.CONTRACT.DEFAULT.SIGPERIOD,
+      "sigReplay": constants.CONTRACT.DEFAULT.SIGREPLAY,
       "sigValidity": constants.CONTRACT.DEFAULT.SIGVALIDITY,
       "msValidity": constants.CONTRACT.DEFAULT.MSVALIDITY,
       "sigQty": constants.CONTRACT.DEFAULT.SIGQTY,
diff --git a/app/lib/indexer.ts b/app/lib/indexer.ts
index c8bd03467407810b4e2c1898eaf792a5a817c3ea..8e600814331437b5341678d0a45fa94ddcd14c11 100644
--- a/app/lib/indexer.ts
+++ b/app/lib/indexer.ts
@@ -121,6 +121,7 @@ export interface CindexEntry extends IndexEntry {
   created_on: number,
   sig: string,
   chainable_on: number,
+  replayable_on: number,
   expires_on: number,
   expired_on: number,
   from_wid: null, // <-These 2 fields are useless
@@ -133,6 +134,7 @@ export interface CindexEntry extends IndexEntry {
   toNewcomer?: boolean,
   toLeaver?: boolean,
   isReplay?: boolean,
+  isReplayable?: boolean,
   sigOK?: boolean,
   created_on_ref?: { medianTime: number },
 }
@@ -145,6 +147,7 @@ export interface FullCindexEntry {
   chainable_on: number
   expires_on: number
   expired_on: number
+  replayable_on: number
 }
 
 export interface SindexEntry extends IndexEntry {
@@ -258,6 +261,7 @@ export class Indexer {
     msValidity:number,
     msPeriod:number,
     sigPeriod:number,
+    sigReplay:number,
     sigStock:number
   }): IndexEntry[] {
 
@@ -473,6 +477,7 @@ export class Indexer {
         unchainables: 0,
         sig: cert.sig,
         chainable_on: block.medianTime  + conf.sigPeriod,
+        replayable_on: block.medianTime  + conf.sigReplay,
         expires_on: conf.sigValidity,
         expired_on: 0,
         from_wid: null,
@@ -970,10 +975,17 @@ export class Indexer {
       ENTRY.toLeaver = !!(reduce(await dal.mindexDAL.reducable(ENTRY.receiver)).leaving)
     }))
 
-    // BR_G44
+    // BR_G44 + 44.2
     await Promise.all(cindex.map(async (ENTRY: CindexEntry) => {
       const reducable = await dal.cindexDAL.findByIssuerAndReceiver(ENTRY.issuer, ENTRY.receiver)
       ENTRY.isReplay = count(reducable) > 0 && reduce(reducable).expired_on === 0
+      if (HEAD.number > 0 && HEAD_1.version > 10) {
+        ENTRY.isReplayable = count(reducable) === 0 || reduce(reducable).replayable_on < HEAD_1.medianTime
+      }
+      else {
+        // v10 blocks do not allow certification replay
+        ENTRY.isReplayable = false
+      }
     }))
 
     // BR_G45
@@ -1477,7 +1489,7 @@ export class Indexer {
   // BR_G71
   static ruleCertificationReplay(cindex: CindexEntry[]) {
     for (const ENTRY of cindex) {
-      if (ENTRY.isReplay) return false;
+      if (ENTRY.isReplay && !ENTRY.isReplayable) return false
     }
     return true
   }
diff --git a/app/modules/config.ts b/app/modules/config.ts
index 5eee626cfdc28582cf8dd43e9e030251596b2c79..fe941d8c5f60d44644f86a6030bfaa119fdc5c05 100644
--- a/app/modules/config.ts
+++ b/app/modules/config.ts
@@ -29,6 +29,7 @@ module.exports = {
     config: {
       onLoading: async (conf:ConfDTO, program: ProgramOptions) => {
         conf.msPeriod = conf.msWindow
+        conf.sigReplay = conf.msPeriod
         conf.switchOnHeadAdvance = CommonConstants.SWITCH_ON_BRANCH_AHEAD_BY_X_BLOCKS
 
         // Transactions storage
@@ -43,6 +44,7 @@ module.exports = {
       },
       beforeSave: async (conf:ConfDTO) => {
         conf.msPeriod = conf.msWindow
+        conf.sigReplay = conf.msPeriod
         conf.switchOnHeadAdvance = CommonConstants.SWITCH_ON_BRANCH_AHEAD_BY_X_BLOCKS
         if (!conf.storage) {
           conf.storage = {
diff --git a/app/modules/prover/lib/blockGenerator.ts b/app/modules/prover/lib/blockGenerator.ts
index dc592a5814a3c649fb93b7cf1f64c8ed13a5b03d..061fc384fc671403b650cfd193a937cb4934db41 100644
--- a/app/modules/prover/lib/blockGenerator.ts
+++ b/app/modules/prover/lib/blockGenerator.ts
@@ -410,7 +410,7 @@ export class BlockGenerator {
               }
             }
             // Already exists a link not replayable yet?
-            let exists = await this.dal.existsNonReplayableLink(cert.from, cert.to);
+            let exists = await this.dal.existsNonReplayableLink(cert.from, cert.to, current.medianTime, current.version)
             if (exists) {
               throw 'It already exists a similar certification written, which is not replayable yet';
             }
@@ -756,7 +756,7 @@ class NextBlockGenerator implements BlockGeneratorInterface {
             let exists = false;
             if (current) {
               // Already exists a link not replayable yet?
-              exists = await this.dal.existsNonReplayableLink(cert.from, cert.to);
+              exists = await this.dal.existsNonReplayableLink(cert.from, cert.to, current.medianTime, current.version)
             }
             if (!exists) {
               // Already exists a link not chainable yet?
diff --git a/app/service/IdentityService.ts b/app/service/IdentityService.ts
index dba4089fc4d5d858dd36b3c26f9a743f3926c05a..b6de424cd0ef5f6ef1edfd3beda329a450111e07 100644
--- a/app/service/IdentityService.ts
+++ b/app/service/IdentityService.ts
@@ -227,7 +227,7 @@ export class IdentityService extends FIFOService {
             written_hash: null,
             block: cert.block_number
           }
-          let existingCert = await this.dal.existsCert(mCert);
+          let existingCert = await this.dal.existsCert(mCert, current)
           if (!existingCert) {
             if (!(await this.dal.certDAL.getSandboxForKey(cert.from).acceptNewSandBoxEntry(mCert, this.conf.pair && this.conf.pair.pub))) {
               throw constants.ERRORS.SANDBOX_FOR_CERT_IS_FULL;
diff --git a/doc/Protocol.md b/doc/Protocol.md
index ba7ac24c84811f70d1a64c0ded1737c613fc35af..6605ee6e54580d6cd1903bccf86da32618fc1f2b 100644
--- a/doc/Protocol.md
+++ b/doc/Protocol.md
@@ -1137,6 +1137,7 @@ udTime0     | Time of first UD.
 udReevalTime0 | Time of first reevaluation of the UD.
 sigPeriod   | Minimum delay between 2 certifications of a same issuer, in seconds. Must be positive or zero.
 msPeriod    | Minimum delay between 2 memberships of a same issuer, in seconds. Must be positive or zero.
+sigReplay   | Minimum delay between 2 certifications of a same issuer to a same receiver, in seconds. Equals to `msPeriod`.
 sigStock    | Maximum quantity of active certifications made by member.
 sigWindow   | Maximum delay a certification can wait before being expired for non-writing.
 sigValidity | Maximum age of an active signature (in seconds)
@@ -1391,6 +1392,7 @@ Each certification produces 1 new entry:
         sig = SIGNATURE
         expires_on = MedianTime + sigValidity
         chainable_on = MedianTime + sigPeriod
+        replayable_on = MedianTime + sigReplay
         expired_on = 0
     )
 
@@ -2274,6 +2276,25 @@ If `count(reducable) == 0`:
 Else:
 
     ENTRY.isReplay = reduce(reducable).expired_on == 0
+
+####### BR_G44.2 - ENTRY.isReplayable
+
+If `HEAD.number > 0 && HEAD~1.version > 10` :
+
+    reducable = GLOBAL_CINDEX[issuer=ENTRY.issuer,receiver=ENTRY.receiver,expired_on=0]
+
+    If `count(reducable) == 0`:
+    
+        ENTRY.isReplayable = true
+    
+    Else:
+    
+        ENTRY.isReplayable = reduce(reducable).replayable_on < HEAD~1.medianTime
+Else:
+
+    ENTRY.isReplayable = false
+
+EndIf
     
 ####### BR_G45 - ENTRY.sigOK
 
@@ -2519,7 +2540,7 @@ Rule:
 
 Rule:
 
-    ENTRY.isReplay == false
+    ENTRY.isReplay == false || ENTRY.isReplayable == true
 
 ###### BR_G72 - Certification signature
 
diff --git a/test/fast/index/v1.0-local-index.ts b/test/fast/index/v1.0-local-index.ts
index acfc7bcc05dba73aca70d214528795e3c42ca9d2..17a8fa7b29605c80b7817be4c8149216240167f0 100644
--- a/test/fast/index/v1.0-local-index.ts
+++ b/test/fast/index/v1.0-local-index.ts
@@ -103,7 +103,8 @@ describe("v1.0 Local Index", function(){
       // We don't care about these in this test
       msPeriod: 0,
       sigPeriod: 0,
-      sigStock: 0
+      sigStock: 0,
+      sigReplay: 0,
     });
   });
 
diff --git a/test/integration/certification/certification-replay.ts b/test/integration/certification/certification-replay.ts
new file mode 100644
index 0000000000000000000000000000000000000000..aee047c9103173bd192f79e4394c32b968b65ac0
--- /dev/null
+++ b/test/integration/certification/certification-replay.ts
@@ -0,0 +1,69 @@
+// Source file from duniter: Crypto-currency software to manage libre currency such as Äž1
+// Copyright (C) 2018  Cedric Moreau <cem.moreau@gmail.com>
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+
+import {assertEqual, writeBasicTestWithConfAnd2Users} from "../tools/test-framework"
+import {assertThrows} from "../../unit-tools"
+import {reduce} from "../../../app/lib/indexer"
+
+describe('Certification replay', () => writeBasicTestWithConfAnd2Users({
+  sigReplay: 3,
+  sigPeriod: 0,
+  sigValidity: 10,
+}, (test) => {
+
+  const now = 1500000000
+
+  test('should be able to init with 2 blocks', async (s1, cat, tac) => {
+    await cat.createIdentity()
+    await tac.createIdentity()
+    await cat.cert(tac)
+    await tac.cert(cat)
+    await cat.join()
+    await tac.join()
+    await s1.commit({ time: now, version: 10 })
+    await s1.commit({ time: now })
+  })
+
+  test('should exist only 1 valid link from cat replyable at t + 3', async (s1, cat) => {
+    const reducableFromCat = await s1.dal.cindexDAL.reducablesFrom(cat.pub)
+    assertEqual(reducableFromCat.length, 1)
+    assertEqual(reduce(reducableFromCat).chainable_on, now) // No delay between certifications
+    assertEqual(reduce(reducableFromCat).replayable_on, now + 3) // Replay of a particular certification <-- What we want to test
+    assertEqual(reduce(reducableFromCat).expires_on, now + 10) // The expiration date of the certification **INITIALLY**
+  })
+
+  test('should reject a replay from cat', async (s1, cat, tac) => {
+    await assertThrows(cat.cert(tac), '{\n  "ucode": 1004,\n  "message": "Already up-to-date"\n}')
+  })
+
+  test('should accept replay if time has passed enought', async (s1, cat, tac) => {
+    await s1.commit({ time: now + 4 })
+    await s1.commit({ time: now + 4 })
+    await cat.cert(tac)
+    const b = await s1.commit({ time: now + 4 })
+    assertEqual(b.certifications.length, 1)
+  })
+
+  test('should exist only 2 CINDEX entries from cat', async (s1, cat) => {
+    const validLinksFromCat = await s1.dal.cindexDAL.findByIssuer(cat.pub)
+    assertEqual(validLinksFromCat.length, 2)
+  })
+
+  test('should exist only 1 valid link from cat', async (s1, cat) => {
+    const reducableFromCat = await s1.dal.cindexDAL.getValidLinksFrom(cat.pub)
+    assertEqual(reducableFromCat.length, 1)
+    assertEqual(reduce(reducableFromCat).chainable_on, now + 4)
+    assertEqual(reduce(reducableFromCat).replayable_on, now + 4 + 3) // Replayable date should have changed!
+    assertEqual(reduce(reducableFromCat).expires_on, now + 4 + 10) // The expiration date should have changed! (this is the interest of a replay)
+  })
+}))
diff --git a/test/integration/tools/test-framework.ts b/test/integration/tools/test-framework.ts
index 325d6bab70581a10afc30a02977391b894a34cdf..9adff77e90dc53949d22d8b6f0b2b5bb969484e7 100644
--- a/test/integration/tools/test-framework.ts
+++ b/test/integration/tools/test-framework.ts
@@ -1,6 +1,7 @@
 import {catUser, NewTestingServer, tacUser, TestingServer, tocUser} from "./toolbox"
 import {TestUser} from "./TestUser"
 import * as assert from 'assert'
+import {Underscore} from "../../../app/lib/common-libs/underscore"
 
 export function writeBasicTestWith2Users(writeTests: (
   test: (
@@ -9,16 +10,30 @@ export function writeBasicTestWith2Users(writeTests: (
   ) => void
 ) => void) {
 
+  writeBasicTestWithConfAnd2Users({}, writeTests)
+}
+
+export function writeBasicTestWithConfAnd2Users(conf: {
+
+}, writeTests: (
+  test: (
+    testTitle: string,
+    fn: (server: TestingServer, cat: TestUser, tac: TestUser, toc: TestUser) => Promise<void>
+  ) => void
+) => void) {
+
   let s1:TestingServer, cat:TestUser, tac:TestUser, toc:TestUser
 
+  const configuration = Underscore.extend({
+    medianTimeBlocks: 1,
+    pair: {
+      pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd',
+      sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'
+    }
+  }, conf)
+
   before(async () => {
-    s1 = NewTestingServer({
-      medianTimeBlocks: 1,
-      pair: {
-        pub: 'HgTTJLAQ5sqfknMq7yLPZbehtuLSsKj9CxWN7k8QvYJd',
-        sec: '51w4fEShBk1jCMauWu4mLpmDVfHksKmWcygpxriqCEZizbtERA6de4STKRkQBpxmMUwsKXRjSzuQ8ECwmqN1u2DP'
-      }
-    })
+    s1 = NewTestingServer(configuration)
     cat = catUser(s1)
     tac = tacUser(s1)
     toc = tocUser(s1)