Technical reference · written to be handed to a machine
Every hook, every file, every endpoint, and what each failure mode really means. This is the companion to the setup guide: that page gets it running, this page is for when it is running and behaving oddly — and for anyone who simply wants to know how a 2012 assistant is talked into answering from a machine in the kitchen.
Select all of it and paste it into whatever assistant you use, together with the symptom and any log lines you have. It is written so that a model reading it cold has the whole system in front of it — both halves, both logs, and the specific vocabulary each one uses — and can reason about the fault instead of guessing at it.
What follows
Two conventions throughout. <server-ip> is the LAN
address of the computer running the server, and
<device-ip> is the iPad's. Commands are marked as belonging
to one machine or the other, because half of every confusing symptom here
comes from running the right command on the wrong computer.
Plain HTTP over the LAN in the middle. Apple's servers are not at either end.
assistantd, the daemon that has always done Siri's actual
work. Everything the tweak does happens inside that one process.
SpringBoard itself is never hooked.assistantd would now open a connection to Apple's
assistant service and refuse to go any further without one. That
service was switched off years ago. The tweak replaces
-[ADAceConnection startWithAceHost:…] so that it
marks itself connected and returns, and isConnected so it
agrees. The session proceeds as though the far end answered. No packet
is sent, and the three ADCommandCenter methods that would
push anything outbound are dropped on the floor.AudioQueueNewInput, so when Siri opens its
input queue the tweak's own callback sits in front of Apple's. Each
buffer is copied aside and then handed straight on, which means Apple's
recording, level metering and endpointing all continue exactly as
before. A refId — a fresh UUID — is minted the moment
recording starts, and it is what ties this request together from here
on.<ServerURL>/converse. That is the only network call
the device makes for the request, it is plain HTTP, and it stays on
your LAN.mynah-server hands the audio to mynah-stt and
gets a transcript; asks the local model in Ollama what kind of question
this is and what the parameters are; fetches the live data from
whichever public source that intent needs; and, if the answer has
pictures in it, downloads and downsizes each of them first so that the
device is only ever given an address on the server itself.SA* instances — the same classes iOS 6
builds for itself when Apple's service answers — marks the
request outstanding so the session will accept them, and pushes them
into the live session through
-[ADSession aceConnection:didReceiveAceObject:], the same
entry point Apple's own replies came in on.Apple's servers are never contacted. Not contacted and ignored, not contacted and failing quietly — the calls that would reach them are removed while the tweak is enabled. This is the whole reason the thing still works when the service behind it does not.
The Siri interface is Apple's. There is no reimplementation, no reskin and no custom view anywhere in this project. The weather widget, the sports board with its line score, the athlete card, the ticker, the movie marquee — all of it is code that shipped on the device in 2012, drawn from objects the tweak fills in.
The package installs eight files in three places, and creates a ninth at runtime:
/Library/MobileSubstrate/DynamicLibraries/
SiriRemastered.dylib the tweak itself (armv7, ~40 KB)
SiriRemastered.plist the filter -- see below
/Library/PreferenceBundles/SiriRemastered.bundle/
Info.plist NSPrincipalClass = SRRootListController
Root.plist the Settings pane's schema
SiriRemastered the pane's compiled binary
icon.png, [email protected]
/Library/PreferenceLoader/Preferences/
SiriRemastered.plist puts the row into Settings.app
created on first save, and left behind on uninstall:
/var/mobile/Library/Preferences/
com.aditya.ios6siriremastered.plist
Substrate reads
/Library/MobileSubstrate/DynamicLibraries/SiriRemastered.plist to
decide where the library goes. It contains exactly one instruction:
{ Filter = { Executables = ( assistantd ); }; }
So the library is loaded into processes named assistantd and
into nothing else. Every other process on the device — SpringBoard
included — never sees it. The tweak's constructor then checks the
process name a second time and returns immediately if it is anything else,
which is belt and braces on the same guarantee.
If this plist is missing, the dylib is never loaded and the tweak is simply absent: no log lines, no Settings behaviour, nothing. That makes it the first thing to check when the tweak seems not to exist.
Every setting lives in one file,
/var/mobile/Library/Preferences/com.aditya.ios6siriremastered.plist.
The Settings pane reads and writes that file directly rather than going
through CFPreferences, and the tweak reads the same file, so there is exactly
one source of truth and no cache to disagree with it.
The pane makes no network calls at all. It does not ask the server what models exist, it does not fetch the effective configuration, and it does not push anything back — earlier builds did, and the cost of a LAN round trip on a 2012 device was visible every time the pane opened. That plist is now the only channel between the pane and the tweak, in both directions. It also means the pane works perfectly well with the server switched off.
| Key | Type | What it does |
|---|---|---|
ServerURL | string | The only required setting. A bare host:port is
accepted and http:// is prepended. Blank means the
tweak stays completely inert, whatever else is set. |
Enabled | bool | Master switch, default on. Only meaningful once
ServerURL has a value: the two are ANDed
together. |
Location | string | Place name for “near me” and unqualified weather. Sent as a form field on every request. A place named in the question beats it. |
Units | string | imperial or metric. Also sent on every
request. |
CardAnswerCardWeatherCardSportsCardStocksCardMoviesCardRestaurants |
bool | One per card type, all default on. Applied on the device as the reply is unpacked, so turning one off suppresses the card and leaves the spoken answer alone. |
ModelOllamaURLSearXNGURLTMDBKeyWolframAppID |
string | Vestigial. loadPrefs() still reads them, nothing ever
sends them, and the Settings pane no longer offers rows for them.
An older device may still carry values here; they have no effect.
Set the model and the keys on the server, in
.env. |
The tweak reads that plist once, in its constructor — that is, at
the moment Substrate loads the library into assistantd, and again
at the start of every recording — the hook on
-[ADSpeechManager voiceControllerDidStartRecording:successfully:]
calls loadPrefs() before anything else it does.
So a change made in Settings takes effect on your next question.
No respring, and no killall. That matters while debugging: flip a
card off, ask again, and the new setting is already in force.
If you want to be certain the library itself has been reloaded — after replacing the dylib, say, rather than after changing a setting — force the daemon to restart:
# on the device, over SSH
killall assistantd
That is a reload, not an outage. assistantd is a
launch-on-demand job: launchd starts it again the next time you hold the home
button. Nothing is lost and nothing needs restarting afterwards.
A respring is never required for this tweak. SpringBoard has none of it loaded, so there is nothing in SpringBoard to refresh. If a piece of advice tells you to respring for a settings change here, it is wrong.
The constructor bails out unless the process is assistantd,
loads the preferences, and installs two function hooks and nine method hooks.
Every one of the method hooks checks the enabled flag first and calls
straight through to the original when it is off — turning the switch
off makes the tweak transparent rather than absent.
AudioQueueNewInput — replaced so that the caller's
input callback is swapped for the tweak's. That callback appends each
buffer to the tweak's own accumulator and then invokes the original
callback, so stock recording and endpointing carry on untouched. The
sample rate and channel count are read from the format description
assistantd passed in rather than assumed —
iOS 6's Siri asks for 16 kHz mono, and whatever it actually
asked for is what gets stamped into the WAV header. The samples are
16-bit PCM.AudioQueueDispose — replaced only so the tweak can
forget a queue it was tracking.| Class | Selector | What the hook does |
|---|---|---|
ADSpeechManager |
voiceControllerDidStartRecording: |
Mints a fresh refId and empties the audio buffer. This is where a request begins. |
ADSpeechManager |
voiceControllerDidStopRecording: |
Wraps the captured PCM in a WAV header and POSTs it to
<ServerURL>/converse, with a 45-second
timeout. |
ADAceConnection |
startWithAceHost:languageCode: |
Stubbed entirely. Calls _setConnected: on itself and
returns without dialling anything. The hook that makes a dead
service irrelevant. |
ADAceConnection | isConnected |
Returns true, so the session believes it has a live connection. |
ADAceConnection | sendCommand:error: |
Swallowed; reports success and clears the error. |
ADCommandCenter |
_sendRequestToServer:_sendCommandToServer:_sendRetryableRequestToServer: |
Dropped. Nothing outbound survives while the tweak is enabled. |
ADCommandCenter | _refIdIsSpeechStart: |
Returns true for the tweak's own refId, so the session treats the injected reply as belonging to a real speech request. |
When the JSON arrives, on the main thread the tweak:
[ADCommandCenter sharedCommandCenter], then its
_session, then that session's
_serverConnection ivar;_addOutstandingRequestId: with the refId, which is
what makes the interface willing to accept objects for it;SASSpeechRecognized carrying the
transcript — this is what puts your words on the screen —
then an SAUIAddViews holding an
SAUIAssistantUtteranceView for the spoken line plus one
snippet per card, and finally an SARequestCompleted to
close the request out.Each injection is a direct call to
-[ADSession aceConnection:didReceiveAceObject:].
The type string on each element of the server's
cards array selects a builder. Note that the athlete card is
gated by the Sports switch rather than one of its own:
| type | Settings switch | Root class built |
|---|---|---|
answer | Answers & Maths | SAAnswerSnippet |
weather | Weather | SAWeatherForecastSnippet |
sports | Sports | a sports snippet, with baseball getting its own matchup class |
athlete | Sports | SASportsAthleteSnippet |
stock | Stocks | SAStockSnippet |
movies, movie | Movies | SAMovieMovieListSnippet, falling back to SAMovieMovieSnippet for a single film |
restaurants, restaurant | Restaurants | a local-search snippet, list or detail depending on the count |
Every object is built by KVC and every step is guarded. A class that does
not exist on this firmware is logged as
MISSING class <name> and that card is dropped; a value of
the wrong type is coerced or skipped; arrays that Apple's own code would
dereference unguarded are never emitted empty. The design rule throughout is
that a missing card is always better than a crash.
mynah-server is a FastAPI service on port 8807,
published on all interfaces because the device has to reach it.
mynah-stt's port 8808 is bound to
127.0.0.1 only — it is a debug port, and the device never
touches it.
| Method | Path | What it is |
|---|---|---|
| POST | /converse |
The one the device calls. Multipart audio in, the reply envelope out. |
| GET | /debug?q= |
The same pipeline with text in place of audio. The best debugging tool in the system. |
| GET | /healthz |
Status of the speech container, the model, the search backend and the movies key. |
| GET | /config |
The effective configuration, secrets redacted. LAN-only. |
| POST | /config |
Writes per-device overrides. LAN-only. |
| GET | /models |
What Ollama actually has installed. LAN-only. Nothing on the device calls it; it is for you, with curl. |
| GET | /img/<sha1>.jpg |
A pre-staged, downsized image served over plain http. |
| GET | /render |
Server-typeset maths and function plots, staged as images. |
| GET | /go/opentable/<token> |
302 redirect to a real OpenTable search. |
POST /converseMultipart form data. The tweak sends codec
(pcm_s16le_16k_wav), lang
(en-US), session (the refId), location
and units when those are set in Settings, and the WAV itself as
the audio part. The server additionally accepts
lat, lon and client_time, which this
tweak does not send.
The reply is always the same envelope, whatever went right or wrong:
{
"transcript": "what's the weather in london",
"speak": "It's 14 degrees and cloudy in London.",
"dialogPhase": "Summary",
"listenAfterSpeaking": false,
"cards": [ { "type": "weather", … } ]
}
"dialogPhase": "Error" marks the failure envelopes. An empty
cards array is a normal, correct answer for conversation,
greetings and honest “no results” replies.
GET /debug?q=…The most useful thing on the server. It runs the complete pipeline
— classify, fetch, build the cards, compose the spoken line —
from typed text, skipping only transcription, and returns exactly the
envelope /converse would have returned. No device, no
microphone, no network between machines.
# on the server machine
curl "http://localhost:8807/debug?q=weather+in+London"
curl "http://localhost:8807/debug?q=AAPL+stock"
curl "http://localhost:8807/debug?q=who+won+the+Yankees+game+last+night"
It also takes units, lat, lon and
location. Always try this before blaming the device. If
/debug gives the right answer, everything except the microphone
and the LAN is working, and the fault is in one of two much smaller places.
GET /healthz{"status":"ok","stt":"Systran/faster-distil-whisper-small.en",
"llm":"gemma4:e4b","search":"duckduckgo",
"movies":"needs TMDB_API_KEY","public_base":"http://<server-ip>:8807"}
"stt":"unreachable" means the speech container is not
answering — usually still downloading its model on a first run. Check
docker compose logs -f mynah-stt.
GET /config and POST /configBoth are refused with 403 unless the caller's address is private,
loopback or link-local, because iOS 6 cannot encrypt anything and these
carry configuration. GET returns the effective configuration
with tmdb_api_key and wolfram_appid redacted to
set (…cd5f) or unset, so you can confirm a
key was typed correctly without it ever being printed. The
overrides_set list tells you which values are coming from a
device override rather than from .env.
POST accepts a fixed key list —
tmdb_api_key, wolfram_appid, model,
ollama_url, searxng_url, public_base,
units, default_location,
default_city, default_lat,
default_lon — and persists them to
/data/config/overrides.json inside the container, which is
./config/overrides.json beside the compose file. Precedence is
override, then .env, then the built-in default. An empty value
clears an override.
Only four settings and the six card switches affect a request.
Server URL and Enabled decide whether the tweak acts at all;
Location and Units ride along as form fields on every
/converse; the card switches are applied on the device as the
reply is unpacked.
The model and the API keys are not among them, and there is no
way to set them from the iPad. POST /config works, but nothing
on the device ever calls it: the Settings pane makes no network calls, and
the routine in the tweak that would have pushed overrides is compiled and
never invoked. Earlier builds of the pane had Model, Ollama
URL, SearXNG URL, TMDB Key and Wolfram AppID rows;
they were removed, both because they cost a LAN round trip on every open
and because those settings belong on the server. A device upgraded from one
of those builds may still have the values sitting in its plist, doing
nothing.
So: the model and the API keys are set on the server, in
.env, and nowhere else. That is also the safer arrangement
— a key typed on the device would cross the network in plain text,
because iOS 6 cannot encrypt anything.
POST /config is still reachable with curl from the LAN, if
you want to change something without editing .env and
restarting. It is simply not a path the device uses.
GET /modelsLAN-only. Returns what Ollama has installed, with the model currently in use first and flagged:
{"models":[{"name":"gemma4:e4b","size":"4.7 GB","current":true},
{"name":"llama3.2:3b","size":"2.0 GB","current":false}],
"current":"gemma4:e4b"}
If Ollama cannot be reached the response carries
"error": "ollama unreachable" rather than failing, which makes
it a quick way to confirm the container can see Ollama at all. The Settings
pane used to populate a model picker from this; it no longer does, and this
endpoint is now purely for your own use with curl.
/img/<40 hex characters>.jpg or
.png — a pre-staged image, served straight off disk.
The filename is the SHA-1 of the upstream URL plus the target width and
format, so the same picture at the same size is always the same file
and a repeated question does no network at all./img?u=<url>&w=<width> — the lazy
path: fetch, downscale, cache, serve. The upstream host must be on the
allowlist; anything else gets 403 and an
img host refused: line in the log./render?tex=… or
/render?plot=…&var=x — maths typeset and
functions plotted on the server, then staged like any other image. This
is how a maths answer arrives as a picture rather than as a line of
text that the answer card would truncate with an ellipsis./go/opentable/<token> — the token is a
base64url-encoded search term and the endpoint 302s to the real
OpenTable search. It exists because the restaurant card's button has to
be a plain-http URL the device can actually open, and OpenTable is
https.iOS 6 cannot load any https URL at all. Not an image, not a link, not a favicon. It is missing the certificate roots the modern web is signed with, and nothing on the device can supply them.
So the server does the loading. Every poster, team logo, headshot and
photograph is fetched by the server, resized to something an A6X decodes
quickly, written into its own on-disk cache, and handed to the device as a
plain http://<server-ip>:8807/img/<hash>.jpg. The
device only ever loads images from your own machine.
Three consequences follow, and between them they explain a good share of everything that goes wrong:
http://. This is not a
preference or a security trade-off you are being offered — the
image addresses are built from that same base, so an
https:// server address fails for the pictures exactly as
it fails for everything else.photo not plain http -> dropped, and omits the field.
The reason is specific: on iOS 6's card code both completion paths
of a failed image load call setImage: with nil, which
wipes the placeholder artwork that had already been drawn. An omitted
image keeps the placeholder; a broken one leaves a hole.MYNAH_STAGE_BUDGET — 3.5 seconds by default —
caps how long the server waits for a card's images before sending the
card anyway. Anything still downloading finishes in the background and
is a cache hit next time.The same rule is why PUBLIC_BASE should be left blank. The
server learns its own address from the Host header on the
device's own request, which is by definition an address the device can reach.
Hard-coding it can only make that worse, and is worth doing only behind a
reverse proxy.
Over USB, from the computer, with libimobiledevice
installed. Every line the tweak prints is prefixed
[SiriRemastered]:
idevicesyslog | grep SiriRemastered
This needs nothing installed on the device and works whether or not SSH is set up, which makes it the reliable route. Leave it running and then ask Siri something.
A healthy request looks like this — values are illustrative, the sequence is not:
[SiriRemastered] loading (server=http://<server-ip>:8807 enabled=YES cards: answer=1 weather=1 sports=1 stocks=1 movies=1 restaurants=1)
[SiriRemastered] v2 hooks installed
[SiriRemastered] AudioQueue tap: rate=16000 ch=1 fmt=0x6c70636d
[SiriRemastered] record START (ok=1) minted refId=1D4F…
[SiriRemastered] record STOP (reason=1): 51200 bytes ch=1 rate=16000 (~1.6s) -> POST flux
[SiriRemastered] flux reply: transcript="what's the weather" speak="It's 14 and cloudy." cards=1
[SiriRemastered] delivered reply for refId=1D4F… (1 card(s))
The diagnosis is in which line you do not see:
| What is missing, or what it says | What that means |
|---|---|
No loading line at all |
The library is not being injected. Substrate missing or broken, the
filter plist absent, or the dylib not on disk. Check that both
files exist under
/Library/MobileSubstrate/DynamicLibraries/. |
loading … server=<unset> |
No Server URL in Settings. The tweak is deliberately inert until one is set, whatever the Enabled switch says. |
loading … enabled=NO |
Enabled is off, or the URL is blank. Every hook is calling straight through to Apple's original. |
No AudioQueue tap line |
assistantd never opened an input queue, so Siri never
got as far as listening. The fault is upstream of the tweak
entirely. |
record START and no record STOP |
Still recording. Endpointing has not fired — usually it is waiting for you to stop talking. |
record stop but no audio captured |
The tap saw no buffers between start and stop: an empty recording, or audio that came through a path the hook did not see. |
-> POST flux and then nothing |
The request left the device and no reply came back inside 45 seconds. Wrong address, wrong network, server down, or a server still thinking. |
flux error: … |
The reason is on the line: connection refused, timed out,
HTTP 500. This is the network or the server answering,
not a device fault. |
flux reply … cards=0 |
The server chose not to draw a card. Nothing is wrong on the
device; ask the same thing of /debug to see why. |
cards=2 then delivered reply … (0 card(s)) |
The device discarded them: that card type's switch is off in
Settings, or a MISSING class line just above says the
class does not exist on this firmware. |
no live session; cannot deliver |
The answer arrived after the Siri interface had been dismissed. Ask again and wait for it. |
docker compose logs -f mynah-server # follow live, Ctrl-C to stop
docker compose logs --tail 200 mynah-server # the last 200 lines
docker compose logs -f mynah-stt # the speech container
docker compose ps # running? or restarting in a loop?
Lines from the service are prefixed [mynah], and two of them
carry most of the diagnostic weight:
[mynah] transcript: "what's the weather in london"
[mynah] intent: weather plan: {…}
The first is what it heard, the second is what it decided that meant.
Between them they separate mishearing from misunderstanding,
which are different problems with different fixes. Everything else in the log
is a named failure of one data source — espn failed:,
tmdb failed:, overpass failed:,
wolfram failed:, ollama tags unreachable:,
stage failed: — and reads plainly.
A container that docker compose ps reports as
restarting is crashing and coming back; the error sits just
above each restart in the log.
This sequence localises almost any fault in four commands, and it matters that it is in this order — each step rules out everything before it:
curl "http://localhost:8807/debug?q=…" on the
server machine. If this is wrong, stop: the device is irrelevant and
the problem is server-side.curl http://<server-ip>:8807/healthz from a
different computer on the same Wi-Fi. If step 1 worked and this
does not, it is the network or the firewall — the same trip the
device makes.curl http://<server-ip>:8807/config — confirm
the server is using the model, keys and location you believe it
is.idevicesyslog | grep SiriRemastered already running.That exact sentence is written on the device, not by the server,
so hearing it proves the tweak is loaded, heard you, tried, and the round
trip failed. The flux error: line immediately before it names
the reason. In order of likelihood:
https:// instead of
http://, a stale IP address, a missing
:8807, or a trailing slash.The other stock sentences tell you which half spoke, which is worth knowing:
| Sentence | Spoken by | Meaning |
|---|---|---|
| “Sorry, I couldn’t reach the server.” | device | The POST failed outright. |
| “Sorry, I didn’t catch that.” | either | The device says it when the reply carried no spoken line; the server says it when the transcript came back empty. The logs tell you which. |
| “Sorry, I didn’t receive any audio.” | server | The request arrived with no audio part. |
| “Sorry, I ran into a problem handling that.” | server | The server threw an exception. [mynah] ERROR: in its
log has the detail. |
Three causes, in the order worth checking:
flux reply line
saying cards=1 followed by
delivered reply … (0 card(s)) is exactly this.
Remember the athlete card is gated by the Sports switch.curl http://<server-ip>:8807/config reports
"tmdb_api_key": "unset" if that is the answer.curl "http://localhost:8807/debug?q=…"
settles it: an empty cards array means the server made
that choice and the device is behaving correctly.Work through it in this order:
http://<server-ip>:8807/img/3f2a….jpg, forty
hex characters. If it does not load there either, this is the
reachability problem above, not an image problem.localhost, the automatic
detection has been overridden — set
PUBLIC_BASE=http://<server-ip>:8807 in
.env and docker compose up -d.img host refused: means the
upstream host is not on the allowlist;
img proxy failed: or stage failed: means the
download itself failed.photo not plain http -> dropped
means the server handed over an https address, which the
tweak refuses on purpose — see
section 05.Two models load lazily. The language model is read into memory by Ollama
on first use, and the speech model unloads itself after
WHISPER_TTL seconds of quiet — 300 by default —
and reloads on the next question. The first request after a start, or after
a lull, pays for both. Ten to sixty seconds is normal and is not a
fault.
If it never gets faster, the model does not fit in memory and the
machine is swapping, which turns seconds into minutes. On Linux, watch
free -h while a question is being answered: if swap is
climbing, move down a model size, set MYNAH_MODEL, and
docker compose up -d.
/Library/PreferenceLoader/Preferences/SiriRemastered.plist
is missing./Library/PreferenceBundles/SiriRemastered.bundle/SiriRemastered
is absent or not executable. The pane is a real compiled bundle that
Preferences.app dlopens; a plist-only bundle cannot
work. Reinstall the package.Root.plist
produced no specifiers, and the device log says so:
[SiriRemastered] Root.plist produced no specifiers
(bundle=…).assistantd is still running with the values it read when it
started. killall assistantd over SSH, or wait for it to be
restarted on demand. Do not respring — there is nothing of this tweak
in SpringBoard. See section 02.
If the change was to Model or an API key on the device, it
will not take effect at any point: those are never transmitted. Set them in
the server's .env and docker compose up -d. See
section 04.
This is the worst thing that can happen, and it is entirely survivable. Nothing is bricked, nothing is lost, and no data is at risk.
The cards are drawn by Apple's card code, which lives in SpringBoard. The tweak does not run there — but the objects it builds are handed across, and a malformed one can crash the process that draws it. When SpringBoard crashes repeatedly, Substrate steps in and boots the device into safe mode: SpringBoard comes up with no tweaks loaded at all, a banner says so, and the device is otherwise completely usable.
Each of the six card types has its own switch in Settings, and that is the reason they are six switches rather than one toggle. If a card misbehaves on your firmware, or with one particular question, you turn that one off and everything else carries on working. It is meant to be the fix, not a preference — one bad card must never mean an unusable device.
ssh root@<device-ip> then
killall SpringBoard.killall assistantd so the change is picked up for
certain.# on the device, over SSH
mv /Library/MobileSubstrate/DynamicLibraries/SiriRemastered.dylib /var/root/
killall assistantd
Siri reverts to stock — which is to say silent — iOS 6
Siri, the device is entirely normal, and putting the file back is one
mv whenever you want it again. The .plist
beside it can stay: with no library to point at, Substrate loads
nothing.The mildest version of all of this, and the right first move if the device is usable: Settings → iOS 6 Siri Remastered → Enabled, off. Every hook then calls straight through to Apple's original and the tweak is transparent, without anything being removed.
If you do hit this, it is worth reporting. The server's
[mynah] log records the intent and the card it built, and the
device's [SiriRemastered] log records what was delivered
immediately before the crash — between them they identify exactly which
card and which question did it.
In Cydia: Installed → iOS 6 Siri Remastered → Modify → Remove, then respring when it offers. That takes away all eight installed files — the library and its filter, the preference bundle, and the PreferenceLoader entry.
One thing is left behind, deliberately:
/var/mobile/Library/Preferences/com.aditya.ios6siriremastered.plist
That is your server address, location, units and card switches, kept so that reinstalling picks up exactly where you left off. Delete it by hand if you want a genuinely clean slate:
rm /var/mobile/Library/Preferences/com.aditya.ios6siriremastered.plist
MobileSubstrate and PreferenceLoader stay, because other tweaks on the device use them. Removing MobileSubstrate is not something to do casually on a jailbroken device.
Nothing on the server is touched by uninstalling on the device. To take that down too, in the server folder on the server machine:
docker compose down # stop the containers
docker compose down -v # and throw away the speech model and image cache
Then delete the folder. Ollama and its models are separate again, and
ollama rm <model> reclaims that disk space.
http://repo.theadipost.com/