Lab Technical Report LAB-TR-0008
What iMessage Keeps After You Edit, Unsend, React or Delete: Database Evidence from Nine iPhone Databases on iOS 18 to 26
SectraTEK Lab, Prudential Associates
research@sectratek.com
Lab Technical Report, preprint, not peer reviewed
Version 1.2 · First published 27 October 2026; this version 28 October 2026 · CC BY 4.0 · doi:10.5281/zenodo.23717930
Abstract
Recent iOS releases let a sender edit, unsend, react to and reply inline to an iMessage, and keep deleted messages recoverable for a period. Examiners must say which of these states a conversation was in, yet the public descriptions we found of how the Messages database (sms.db) records each action rest largely on single-device observation and tool documentation. We state each storage model as equalities that must hold if the model is right and count the exceptions over every instance in nine sms.db databases from iPhone full file system extractions (iOS 18.1.1 and 26.2.1 to 26.6.2). The edit-history model held: the time of the last stored version equals date_edited on 466 of 466 edited messages. Unsent messages keep their row but not their text. Three plausible readings failed. reply_to_guid is filled on 73 percent of the rows of one iOS 26.5.2 database and equals the reply originator in only 56 of 1,360 replies, so it does not mark replies; some Recently Deleted rows carry a delete_date more than 30 days before the copy, so, if delete_date records the time of deletion (not tested), they are not confined to 30 days; and most reaction removal rows have no matching add row, as exporter documentation predicts. On these databases, replies should therefore be identified by the thread originator, not by reply_to_guid.
CCS Concepts • Applied computing → System forensics.
Keywords iMessage, sms.db, mobile forensics, iOS, edit history, unsent messages, reactions, Recently Deleted, tool validation
ACM Reference FormatDaniel Addai. 2026. What iMessage Keeps After You Edit, Unsend, React or Delete: Database Evidence from Nine iPhone Databases on iOS 18 to 26. Lab Technical Report LAB-TR-0008, version 1.2. SectraTEK Lab, Prudential Associates. Preprint, not peer reviewed. https://lab.sectratek.com/research/reports/lab-tr-0008/ DOI: https://doi.org/10.5281/zenodo.23717930
1 Introduction
Apple Messages lets a sender edit a sent iMessage and take it back after sending [2], react to a single message, reply inline to an earlier one, and recover deleted messages for a period. Each action changes what an examination of the Messages database can show. An edited message may carry its earlier wording; an unsent message may carry nothing but a trace that it existed; a deleted message may remain in the file after it has left the screen. Examiners are asked to say which of these states a conversation was in, and how sure they are.
A motivating example. Suppose an examiner must list the inline replies in a conversation. The message table has a column named reply_to_guid, and the name invites the reading that a filled value marks a reply. In one iOS 26.5.2 database we studied, that column holds a guid on 49,981 of 68,136 rows, including messages that are not replies. Replies identified by the thread originator number 1,360, and the two columns agree on only 56 of them (Listing 1). A reading built on the column name would therefore treat 73 percent of the rows as replies.
Listing 1: Schematic of the two readings of a reply (invented row names and guids; counts from the iOS 26.5.2 database).
row thread_originator_guid reply_to_guid m1 (empty) <guid-1> m2 <guid-2> <guid-2> m3 <guid-2> <guid-3> or empty m1 not a reply, yet reply_to_guid is set m2 reply; the two columns are equal m3 reply; the two columns are not equal iOS 26.5.2 database: reply_to_guid set 49,981 of 68,136 rows replies (originator set) 1,360 columns equal 56 of 1,360 replies
The problem: readings by analogy. The public guidance we found is mostly practitioner-written. It describes the layout seen at one release, for example which column holds the edit history and that the recall time is kept in date_edited [12], and it is easy to extend by analogy to a column name. Whether such a reading holds on iOS 18 and iOS 26, and on more than one database, is an empirical question that a single hands-on test cannot settle.
Approach. We write each storage model as equalities that must hold if the model is right, run them as checks over every instance in the databases, and report every exception. A check that holds on all instances does not prove what a field means, but a check that fails rejects the model for those databases. We ask three research questions about the Messages database of iPhones running iOS 18 and iOS 26.
- RQ1 (Representation). How are edits, unsent parts, reactions, inline replies and Recently Deleted messages represented, and is the representation consistent across the releases we hold?
- RQ2 (Assumed readings). Which commonly assumed fields or intervals do not behave as assumed?
- RQ3 (Recoverability). What can be said about whether removed content is recoverable from the database alone?
Contributions. This report makes four contributions.
- Nine consistency checks, each an equality that a storage model predicts, and their outcome on nine databases; three of the nine fail (Sections 3.1 and 5).
- Three observations that contradict plausible readings:
reply_to_guiddoes not mark replies, some Recently Deleted rows carry adelete_datemore than 30 days before the copy (they outlast the 30-day default only ifdelete_daterecords the time of deletion, not tested), and reaction removal rows rarely keep their add row, as the exporter documentation also predicts (Sections 5.3 to 5.5). - Five rules for reading these records in an examination, derived from the checks (Section 6.2).
- Eight read-only parser artifacts that run the checks over a whole database, built on a shared decoder that returns an empty value instead of raising on unreadable input, with a synthetic database generator and unit tests that make the checks repeatable (Sections 3.2 and 3.4).
Scope. All results describe the nine sms.db databases studied. “Nine” counts databases, not people. The files come from nine different phones, subject to the condition stated in Section 7, and the usage behind each is unknown. The two observations we did not find reported elsewhere, on reply_to_guid and on retention, each rest on a single database, the iOS 26.5.2 one. Most checks replicate published descriptions with counts; Section 6.1 separates what replicates from what is new, and Section 7 states the limits.
2 Background and Related Work
This section describes the database structures that record each action, then places the work among practitioner descriptions, open-source parsers and work on validating forensic tools.
2.1 How sms.db records each action
The Messages database is an SQLite file, sms.db, with one row per message in a message table and join tables to chats and attachments. A message can hold several parts, numbered from 0, and edits and unsends apply to a part. Text typed on recent releases is often not in the text column but in attributedBody, an archived attributed string. A binary property list in message_summary_info holds short-coded keys; those relevant here are ec, the edit-history list (part index to a list of versions, each with a time d and archived text t), otr (original text range per part) and rp (retracted parts). The open-source imessage-exporter project documents ec, otr and rp and the order of the version list, original first and current last [8], [9].
A reaction is a message row of its own whose associated_message_type and associated_message_guid point at the target message; we call a row that adds a reaction an add row and a row that removes one a removal row. An inline reply names the message it answers, its originator, in thread_originator_guid. Recently Deleted messages are tracked in chat_recoverable_message_join and recoverable_message_part; the feature was introduced with iOS 16 [12], and the oldest database we hold is iOS 18.1.1. Figure 1 maps each action to its storage and to the outcome of the check applied to it.
sms.db (first two lines of each row) and the outcome of the check applied to it (last two lines). Counts are over the nine databases; values marked * are from the single iOS 26.5.2 database. Source: artifact notes [1].2.2 Practitioner and vendor descriptions
The closest prior work is practitioner writing on the iOS 16 changes (Table 1). A vendor blog post describes the same fields we examine: for an unsent message the text and attributedBody values are empty and the change time is in date_edited; for an edited message earlier versions are in the property list under ec; Recently Deleted messages are tracked by chat_recoverable_message_join and persist for 30 days by default [12]. The post reports no counts over several databases and does not examine the reply columns. A vendor post on a commercial tool's handling of iOS 16 sent messages [4], an article on recognising deleted messages from gaps in the row identifiers [10] and an early forum thread on the iOS 16 sms.db changes [5] cover related ground. We read [10] in full; [4] and [5] we confirmed only from their titles and search summaries, and no result here relies on them.
| Source | Kind | What it describes | Read |
|---|---|---|---|
| Vance [12] | Vendor blog | Unsent: text and attributedBody empty, change time in date_edited; edits under ec; Recently Deleted in chat_recoverable_message_join, 30 days by default; date_retracted apparently unused | In full |
| imessage-exporter [8], [9] | Open-source exporter | Keys ec, otr, rp; version order; reaction type mapping; add row removed with the reaction | Three source files at a pinned commit |
| Rhodes [10] | Practitioner article | Deleted messages recognised from gaps in row identifiers; not the Recently Deleted tables | In full |
| Cellebrite [4] | Vendor post | A commercial tool's handling of iOS 16 sent messages | Title and search summary |
| Forensic Focus [5] | Forum thread | Early testing of the iOS 16 changes for unsent and edited messages | Title and search summary |
| Apple [2] | User guide | The unsend and edit features for users | Title only; body not retrieved |
2.3 Open-source parsers
imessage-exporter reads the same database and documents its fields from the source of the format [8]. We used it for field meaning only and copied no code; its licence is GPL-3.0. iLEAPP, with its Android counterpart ALEAPP, is a Python parser suite for forensic extractions [3].
2.4 Validating forensic tools
The case for testing forensic tools and their assumptions, rather than trusting them, is made in the literature on tool reliability [7] and in the broader research agenda of the field [6]. Our checks are a small instance of that practice applied to a data format.
Position of this work. This work differs from the sources in Table 1 in method rather than in subject. We restate the published descriptions, and the readings that column names suggest, as checks; count the instances that could break each; and report the exceptions. Where a check holds, the result is a replication with counts; where it fails, it is an observation on the databases we hold.
3 Method
This section defines the checks, the parser artifacts that run them, the independent recount of their output and the synthetic fixtures.
3.1 Storage models and falsifiable checks
For each action we wrote down a storage model from the format documentation [8], [9] and from inspection of the databases, and derived equalities that must hold if the model is right. For example, the time of the last item in an edit-history list should equal date_edited. We counted the messages for which each equality holds and listed every exception. Table 2 lists the nine checks, C1 to C9. C6 and C7 state plausible readings rather than documented ones: that a removal row leaves its add row, and that reply_to_guid marks replies. C9 tests the 30-day default retention that a vendor post gives for Recently Deleted messages [12]. A check that holds on all instances is only as strong as the number of instances that could have broken it.
| Check | Action | Prediction of the model |
|---|---|---|
| C1 | Edit | First version time equals message date |
| C2 | Edit | Last version text equals current text |
| C3 | Edit | date_edited equals last version time |
| C4 | Unsend | An unsent part has no text |
| C5 | React | Reaction name matches the verb of the system text |
| C6 | React | A removal row has its add row stored |
| C7 | Reply | reply_to_guid marks replies |
| C8 | Delete | Recently Deleted rows are not linked to a chat |
| C9 | Delete | Recently Deleted rows are within 30 days |
3.2 Parser artifacts
The checks run inside eight read-only artifacts (Table 3), modules that each present one kind of record as a table in the tool's report. They share a helper that decodes the archived strings, the binary property lists and the keyed archives. Every decoder returns an empty value on input it cannot read and never raises, so one damaged blob costs one cell and not the artifact. Timestamps are Cocoa times: nanoseconds since 2001-01-01 UTC above a threshold of 1015, seconds below it, converted to UTC. Values the databases store but we cannot interpret are reported as stored.
| Artifact | Stored fields read | Format decoded |
|---|---|---|
| Reactions | associated_message_type, associated_message_guid, associated_message_emoji | Guid forms p:<part>/<guid>, bp:<guid> |
| Edit History | message_summary_info keys ec, otr; attributedBody; date_edited | Binary plist; archived string |
| Unsent Message Parts | message_summary_info keys rp, otr; date_edited; date_retracted | Binary plist |
| Inline Replies | thread_originator_guid, thread_originator_part | Plain columns |
| Recently Deleted Messages | chat_recoverable_message_join | Cocoa nanoseconds |
| Recently Deleted Message Parts | recoverable_message_part | Archived string |
| Link Previews | payload_data (richLinkMetadata or metadata) | Keyed archive |
| iMessage App Messages | balloon_bundle_id, payload_data | Keyed archive |
3.3 Independent recount
Row counts of every artifact on every re-run database were recomputed with separate code: plain SQL on sms.db with the write-ahead log applied [11], plistlib for the property lists, and a small keyed-archive walker for the link-preview payloads. The first version of the walker was a heuristic and disagreed with the artifact by 16, 4 and 26 rows on three databases; a proper archive reader resolved each difference in favour of the artifact. The recount is therefore a check on both sides, not only on the artifact.
3.4 Synthetic fixtures
A generator writes a fabricated sms.db with reserved documentation phone numbers, example domains, fixed sentences and fixed dates. Twelve unit tests and eight synthetic test cases, one per artifact with recorded baselines, run against it. No fixture contains data from a real database.
4 Evidence and Setup
This section describes the databases, the tools and what counts as agreement.
4.1 Databases
The evidence is nine sms.db files from full file system extractions of iPhones in the Lab's research collection, opened read-only and labelled D1 to D9 by release (Table 4): iOS 18.1.1 (one), iOS 26.2.1 (one), iOS 26.3.1 (three), and iOS 26.5.2, 26.6, 26.6.1 and 26.6.2 (one each). Their usage, language, contacts and message volume are unknown and uneven, as the row counts show.
| Database (iOS) | React. | Edits | Unsent | Replies | Del. msgs | Del. parts | Links | Apps |
|---|---|---|---|---|---|---|---|---|
| D1 18.1.1 (a) | 362 | 161 | 30 | 173 | 0 | 0 | 55 | 61 |
| D2 26.2.1 | 79 | 48 | 59 | 86 | 0 | 0 | 144 | 4 |
| D3 26.3.1 | 23,375 | 20 | 0 | 2,852 | 0 | 0 | 2,186 | 313 |
| D4 26.3.1 | 37 | 10 | 1 | 48 | 1 | 1 | 417 | 4 |
| D5 26.3.1 | 89 | 53 | 61 | 107 | 0 | 0 | 158 | 7 |
| D6 26.5.2 | 4,174 | 441 | 31 | 1,360 | 86 | 10 | 2,275 | 31 |
| D7 26.6 | 148 | 30 | 0 | 59 | 102 | 0 | 14 | 0 |
| D8 26.6.1 | 1,301 | 117 | 4 | 745 | 2 | 0 | 81 | 0 |
| D9 26.6.2 | 1,251 | 66 | 6 | 605 | 13 | 0 | 83 | 27 |
| Total | 30,816 | 946 | 192 | 6,035 | 204 | 11 | 5,413 | 447 |
React. = reactions, including sticker rows (type 1000) and types 2000 to 3007; Edits = edit-history versions, one row per stored version, so the 946 rows belong to 466 edited messages; Unsent = unsent parts, one row per part in the retracted list; Del. msgs and Del. parts = Recently Deleted messages and message parts; Links = link previews; Apps = iMessage app messages. Rows are listed per chat, so a message joined to two chats appears twice. (a) Counts for D1 come from an earlier run of the same artifacts and were not recounted (Section 4.3); D2 to D9 were recounted. Source: artifact notes and recount output [1].
4.2 Tools and versions
The artifacts were run with the parser suite's command-line tool in zip mode; the code base they were built on and its continuous-integration result are given in Artifact Availability [1]. For field meaning, the artifacts' notes quote pinned permalinks to the imessage-exporter source at commit 4d90fc8d [9].
4.3 What counts as agreement
We did not observe the actions that produced these records, so the stored data are the reference. Agreement takes two forms: the equalities of Section 3.1, which a correct model must satisfy, and the independent recount of row counts, which shows that the parser reports what the file contains. Eight of the nine databases, D2 to D9, were re-run and recounted with the final version of the artifacts. The iOS 18.1.1 database (D1) was used in an earlier run of the same artifacts; its counts, and the figures in the artifact notes derived from it, were not recounted.
5 Results
The edit-history and unsend models held, with explained exceptions, and three plausible readings failed. Table 5 answers each research question and Table 6 gives the outcome of every check; the subsections give the evidence, one action at a time.
| Question | Answer | Evidence |
|---|---|---|
| RQ1 Representation | No check of the documented layout failed, and the edit-history checks held on iOS 18.1.1 as on iOS 26: edit history in ec, unsent parts in rp, reactions as rows of their own, Recently Deleted rows outside any chat. Replies are marked by thread_originator_guid. | Sec. 5.1 to 5.5; C1 to C5, C8; Figures 1 and 2 |
| RQ2 Assumed readings | reply_to_guid does not mark replies; Recently Deleted rows can have a delete_date more than 30 days before the copy (they are not confined to 30 days if delete_date is the time of deletion, not tested); most removal rows have no add row, as documented; date_retracted was 0 on every row of all nine databases. | Sec. 5.2 to 5.5; C6, C7, C9; Figure 3 |
| RQ3 Recoverability | Partly. Edited messages keep earlier wording in the edit-history list; unsent parts keep the row and a stored length but not the text; Recently Deleted messages keep their wording in attributedBody, and a message removed from Recently Deleted leaves no row. | Sec. 5.1, 5.2 and 5.5 |
| Check | Outcome | Verdict |
|---|---|---|
| C1 | 463 of 463 (edits at part 0) | Holds |
| C2 | 463 of 466; 3 exceptions at parts other than 0 | Holds, exceptions explained |
| C3 | 466 of 466, within 2 ms | Holds |
| C4 | 184 of 184 messages | Holds |
| C5 | 28,076 of 29,574; 0 match another type; 1,498 unreadable | Not contradicted |
| C6 | 9 of 76 | Fails, as documented |
| C7 | Set on 49,981 of 68,136 rows; equals originator in 56 of 1,360 | Fails (one database) |
| C8 | 204 of 204 | Holds |
| C9 | 60-day span in one database; part rows 1 to 3 years old | Fails (one database) |
5.1 Edit history
The edit-history layout matched the published description on every instance we hold [8], [9]. The nine databases hold 466 edited messages, each with one ec entry. Of these lists, 452 hold two versions and 14 hold three, so a list is not limited to a single edit, and none holds more than three (Figure 2). On the 463 messages edited at part 0, the time of the first stored version equals the message date (C1). The time of the last stored version equals date_edited on all 466, to within 2 ms (C3), and the last version's text equals the current text in attributedBody on 463 of 466 (C2).
The three exceptions to C2 are edits to a part other than 0 of a message with several parts. There attributedBody holds the whole message and not the one part, so the exceptions are consistent with the model once part structure is taken into account. We read this result as a replication with counts, not as a new finding.
ec) and the equalities tested. Version 3 is drawn dashed because it exists in 14 of 466 lists; the bracket marks the last version of each list, which is version 2 in 452 lists and version 3 in 14. Source: artifact notes [1].5.2 Unsent messages
An unsent part keeps its row and loses its wording. In seven of the nine databases, 184 messages had a retracted-part entry (rp) covering 192 parts. All 184 had an empty text (C4), and none had attributedBody unless another part of the message was kept. The only trace of the removed wording in these columns is the stored length of the original text range (otr, value le), which gives a length and not the text.
For these 184 messages, date_edited lay between 2.8 and 118.7 s after the message date. This is consistent with a brief unsend window, which Apple's user guide describes for the feature [2]. We could not retrieve the body of that page; search summaries of it give two minutes, which would contain the observed maximum of 118.7 s, but we have not checked the interval against Apple's own text. date_retracted was 0 on every row of all nine databases, so on these releases the rp entry, not the retraction column, marks an unsent part. This agrees with the vendor observation that the column does not appear to be used [12].
5.3 Reactions
The documented mapping of reaction names was not contradicted. Reaction names (Loved, Liked, Disliked, Laughed at, Emphasized, Questioned) follow the numeric associated_message_type mapping documented in imessage-exporter [9], and we cross-checked it against the system-written text of the same rows (C5). Of 29,574 rows of types 2000 to 2005, 28,076 (94.9 percent) start with the verb that matches the type, none starts with a verb that belongs to another type, and 1,498 start with something else, such as empty text or another language. The cross-check cannot establish meaning for those 1,498 rows.
A removal row rarely has its add row (C6). Stored types 3000 to 3007 are read as the removal of the reaction numbered 1000 lower, as documented in the same project [9]. Of 76 removal rows, 9 have an add row of the matching type for the same target still in the table; for the other 67 the add row is not in the database. The documented behaviour is that the add row is removed when a reaction is removed, so a missing add row is the common state and says nothing about whether a reaction was ever added.
5.4 Inline replies
reply_to_guid does not mark replies on iOS 26.5.2 (C7). In the iOS 26.5.2 database (D6) the column holds a 36-character guid on 49,981 of 68,136 rows (73.4 percent), including messages that are not replies, and it equals thread_originator_guid in only 56 of the 1,360 replies there (4.1 percent; Figure 3). The artifact therefore identifies replies by thread_originator_guid. We did not determine what reply_to_guid does record.
Across the nine databases there are 6,035 such replies, and for 6,017 the originator row was still in the database. The third number of thread_originator_part equals the length of the originator's text in 4,913 of those 6,017 (81.6 percent), so we report that field as stored and do not interpret it.
5.5 Recently Deleted
Recently Deleted rows keep their wording, and some have a delete_date more than 30 days before the copy. Five of the nine databases hold 204 rows in chat_recoverable_message_join. The message rows stay in the message table with their wording in attributedBody, and none of the 204 has a row in chat_message_join, which separates them from messages shown in a conversation (C8).
In D6 the earliest and latest delete_date are 60 days apart, so, if delete_date records the time of deletion (not tested), the oldest of those rows was at least 60 days old when the database was copied, and the rows cannot be assumed to fall within the 30 days that a vendor post gives as the default retention (C9) [12]. Two databases hold 11 part rows in recoverable_message_part, where the rest of the message is still in its conversation; in D6 their delete_date values are one to three years before the extraction. The database therefore retains rows beyond the interval the feature advertises. Whether the operating system would still offer them to the user, we did not test. A message removed from Recently Deleted leaves no row, so an empty result is not evidence that nothing was deleted.
5.6 Link previews and app messages
The artifacts decoded 5,413 link previews and 447 app messages, the latter in seven of the nine databases, from keyed archives. The title and summary of a preview come from the linked page and were stored by Messages; they are not text typed by a person, and a preview does not show that the link was opened. These two artifacts carry no consistency check beyond row counts.
5.7 Independent recount
All 64 counts, eight artifacts on each of the eight re-run databases, equal the independent recount. The artifacts also passed the project's own checks on output quality; the first pass listed 45 distinct findings of empty, constant or identical columns (for example no named group chat, a single iMessage line, or a link that is also the message text), and each is now named in the artifact notes. The recount verifies row counts only: the consistency statistics in Table 6 were computed during artifact development and were not each re-derived by a second method.
6 Discussion
The results matter less for the layouts they confirm than for the readings they rule out. This section separates the two, states what they mean for an examination, and draws one lesson for parser testing.
6.1 What replicates and what is new
The edit-history layout, the use of rp for unsent parts and the Recently Deleted tables were already described [8], [9], [12]; checks C1 to C5 and C8 replicate those descriptions with counts on more databases. The observations we did not find reported elsewhere are the failures of C7 and C9 and the three exceptions to C2 explained by multi-part messages. C6 fails as the exporter documentation predicts, since the add row is removed with the reaction [9]; our contribution there is the count. The new observations are warnings against assuming the opposite, not measured prevalences. The artifacts and tests are engineering that makes the checks repeatable.
6.2 Consequences for examination
- An edit-history list is a record of what Messages kept, and the number of versions may not equal the number of edits a person made. We observed at most three versions.
- An unsent message is visible as a row with no text and a retracted-part entry; its stored original length is the only trace of the wording in these columns.
- A reaction removal without an add row is expected. The absence of an add row should not be reported as evidence that no reaction occurred.
- Replies should be identified by the thread originator, not by
reply_to_guid, on the releases we examined. - A Recently Deleted row indicates a deletion consistent with the stored time; it does not show who deleted the message, and it does not imply the row is recent. An empty table is not evidence of no deletion.
6.3 Testing a parser against its own model is weak
Tool-testing literature argues for tests that expose assumptions [7]. Our equalities are cheap to state and to run, and three of the nine failed. The recount also exposed a defect in the first version of our own recount walker. Together these suggest that a parser's own tests, written from the same model as the parser, are weak evidence against a wrong model.
7 Threats to Validity
Internal validity. Counts were produced by the parser under study and, for row counts, re-derived with separate code; agreement shows that the parser reports what the file holds, not that the model matches what the operating system meant. The consistency statistics were not independently re-derived. The D1 (iOS 18.1.1) counts were not recounted in the final run.
Construct validity. A row is a record the device stored, not proof of a user action, and it does not show who edited, unsent, reacted or deleted, or from which device. Times are as stored and converted to UTC. The 73 percent for reply_to_guid and the 60-day span are single-database statistics and depend on how the device's user used Messages.
External validity. The sample is nine databases of unknown usage, mostly from one family of releases (iOS 26). We have no iOS 16 or 17 data, none from iPad or Mac, and none acquired by backup or logical methods; results are stated per class and are not generalised. We did not record which extraction each file came from. A comparison of hashed device identifiers found that the full file system extractions of the Lab’s collection that match the nine releases (one each for six releases, six for iOS 26.3.1) are all different phones, so the nine files come from nine phones provided that they were taken from these extractions.
Conclusion validity. No statistical inference is made; percentages describe the sample only. A model that survives every check is not proven; a failed check rejects the model for these databases.
Limitations. From the stored records alone we cannot say how a given action maps to a given row, how many edits a person made, whether Recently Deleted rows are still offered to the user, or what the operating system does on a synced second device. Our confidence is therefore moderate for the edit-history and unsend layouts, and low for retention and for how often each feature is used, because nine databases of unknown usage are not a sample.
Reproducibility. The fixtures are synthetic and released with the code. Anyone can run the released artifacts on a comparable sms.db or on the generated database; the real-data results can be checked only with comparable databases.
8 Conclusion and Future Work
Column names and single-release descriptions invite readings of the Messages database that may not hold. Stated as checks and run over nine iPhone sms.db files, the edit-history and unsend layouts behave as published, with exceptions we can explain, while one plausible reading does not hold (reply_to_guid is not a reply marker) and another fails if delete_date records the time of deletion, which was not tested: Recently Deleted rows with a delete_date more than 30 days before the copy outlast the 30-day default. A third, that a reaction removal leaves its add row behind, fails as the exporter documentation predicts. Stating a storage model as equalities and counting the exceptions is a test that any parser of this database can run, open-source parsers included. What remains open is whether the two readings that failed on one database fail more widely, and how the earlier releases iOS 16 and 17 store these actions.
Future work, in order of importance: (1) databases from iOS 16 and 17, and from iPad and Mac, to test the claims on earlier releases and other devices; (2) a test of C7 and C9 on more databases, since each now rests on one; (3) a larger and better described corpus, so that the fractions in Figure 3 can be reported with an interval and a stated population.
Artifact Availability
The eight artifacts, the shared decoding helper, the synthetic database generator, the twelve unit tests and the eight synthetic test cases are released as part of iLEAPP [3] under the MIT licence, merged through pull request #2576 [1]. They were developed against the iLEAPP main branch at the time of the pull request, and its continuous integration passed 26 of 26 jobs on the final version of the change. The real databases are not released.
Data Handling Statement
Test data were of two kinds: (1) full file system extractions, sysdiagnose archives and bug reports of research devices (test devices) provided to the Lab by their owners and held in its research collection, used read-only for research with the owners' permission and never copied into any repository, fixture, paper or public file; (2) fully synthetic databases written by the author with invented values (reserved example domains, documentation phone numbers, placeholder identifiers). The nine sms.db databases in this paper come from full file system extractions of kind (1). Device models and the identities of device users are withheld, and only aggregate counts, device classes, operating system versions and table, column and key names are reported. No names, numbers, accounts, message text, serial numbers, network names, link addresses or file paths from the real data appear in this report; the guids in Listing 1 are invented. A leak scan compared the released fixtures with the real sources before release. No offensive technique, exploit or credential-recovery method is described.
Declaration of AI Assistance
The parser code, the data checks and the first draft of this report were produced with AI assistance (Anthropic Claude), as was the revision of the text into its present form. The author directed the work, reviewed the results and the text, and is responsible for the content.
References
- Daniel Addai. 2026. SMS & iMessage: reactions, edit history, unsent parts, replies, recently deleted, link previews and app messages. iLEAPP pull request #2576, merged October 25, 2026. Retrieved October 27, 2026 from https://github.com/abrignoni/iLEAPP/pull/2576
- Apple. [n. d.]. Unsend or edit messages on iPhone. iPhone User Guide, Apple Support. Retrieved October 27, 2026 from https://support.apple.com/guide/iphone/unsend-or-edit-messages-iphe67195653/ios (Page and title confirmed; the body could not be retrieved, so the unsend limit was seen in search summaries only.)
- Alexis Brignoni et al. [n. d.]. ALEAPP and iLEAPP. GitHub repositories (MIT licence). Retrieved October 27, 2026 from https://github.com/abrignoni/ALEAPP and https://github.com/abrignoni/iLEAPP
- Cellebrite. [n. d.]. How to Examine Sent Messages Using New Mobile Forensics Features for iOS 16 in Physical Analyzer. Tip Tuesday series. Retrieved October 27, 2026 from https://cellebrite.com/en/how-to-examine-sent-messages-using-new-mobile-forensics-features-for-ios-16-in-physical-analyzer (Existence and subject confirmed in search results; the page redirected to an image and was not read in full.)
- Forensic Focus. [n. d.]. changes to sms db in ios 16 for unsent and edited messages. Forum thread. Retrieved October 27, 2026 from https://www.forensicfocus.com/forums/general/changes-to-sms-db-in-ios-16-for-unsent-and-edited-messages/ (Existence and subject confirmed in search results; the page returned HTTP 403 and was not read.)
- Simson L. Garfinkel. 2010. Digital forensics research: The next 10 years. Digital Investigation 7 (2010), S64–S73. https://doi.org/10.1016/j.diin.2010.05.009
- Graeme Horsman. 2019. Tool testing and reliability issues in the field of digital forensics. Digital Investigation 28 (2019), 163–175. https://doi.org/10.1016/j.diin.2019.01.009
- ReagentX. [n. d.]. imessage-exporter. GitHub repository (GPL-3.0). Retrieved October 27, 2026 from https://github.com/ReagentX/imessage-exporter (Read as documentation only; no code copied.)
- ReagentX. [n. d.]. imessage-exporter source files
imessage-database/src/message_types/edited.rs,message_types/variants.rs(lines 18 to 28) andtables/messages/message.rs(lines 1243 to 1265), commit 4d90fc8d20a745c0a8acc1e01c0631c4bab89cb4. Retrieved October 27, 2026 from https://github.com/ReagentX/imessage-exporter/blob/4d90fc8d20a745c0a8acc1e01c0631c4bab89cb4/imessage-database/src/message_types/edited.rs (All three files fetched at this commit: key and ordering notes in edited.rs, the removal behaviour in variants.rs and the type mapping in message.rs.) - A. Rhodes. 2023. Forensic challenges and solutions with the new iPhone message deletion. Vestige Ltd., June 13, 2023. Retrieved October 27, 2026 from https://www.vestigeltd.com/resources/articles/forensic-challenges-and-solutions-with-the-new-iphone-message-deletion/ (Fetched and read; it covers row-identifier gaps, not the Recently Deleted tables.)
- SQLite. [n. d.]. Write-Ahead Logging. Retrieved October 27, 2026 from https://www.sqlite.org/wal.html
- C. Vance. 2022. iOS 16: What Digital Investigators Need to Know. Magnet Forensics blog, September 12, 2022. Retrieved October 27, 2026 from https://www.magnetforensics.com/blog/ios-16-what-digital-investigators-need-to-know/