<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Phase 8 — Calling — Arc Plan</title>
<style>
body { font-family: system-ui, sans-serif; max-width: 760px;
margin: 1.5em auto; padding: 0 1em; line-height: 1.5; color: #1a1a1a; }
h1 { font-size: 1.4em; }
h2 { font-size: 1.1em; margin-top: 1.4em; }
ol { margin: 0.3em 0; } li { margin: 0.25em 0; }
ol.outline { list-style-type: upper-roman; }
ol.outline ol { list-style-type: upper-alpha; }
ol.outline ol ol { list-style-type: decimal; }
ol.outline ol ol ol { list-style-type: lower-alpha; }
.check { color: #1a7f37; font-weight: bold; }
.next { color: #b35900; font-weight: bold; }
.review { color: #0969da; font-weight: bold; }
.working { color: #b35900; font-weight: bold; }
.nope { color: #82071e; font-weight: bold; }
.q { color: #8250df; font-weight: bold; }
code { background: #f2f2f2; padding: 0 0.25em; border-radius: 3px; }
em { color: #555; }
</style>
</head>
<body>
<h1>Phase 8 — Calling</h1>
<p><em>Markers: <span class="next">▶</span> being worked on,
<span class="review">⚑</span> implemented and waiting to be
reviewed,
<span class="check">✓</span> done,
<span class="check">✓</span><span class="nope">✗</span> closed
by deciding not to do it, <span class="q">?</span> open decision,
unmarked = planned.</em></p>
<h2>What this arc is for</h2>
<p>Two users can call each other with audio or video from the messages
screen. The call runs between the two browsers; Yioop carries the offers,
the answers and the candidates between them. A call between two people at
home usually cannot be made directly, since each is behind a router that
lets nothing in unasked, so a relay has to stand where both can reach it.
Yioop ships one, TurnSite, and nothing starts it: GROUP_CALL_ICE_SERVERS
is null, so the browser is handed no relay at all and the call has only
direct paths to try. Calls worked for Chris when coturn was set up beside
the site and the setting was filled in by hand. This arc brings Yioop own
relay up, wires it to the code that is already there, and then debugs
that code with a relay in place rather than without one.</p>
<h2>Plan</h2>
<ol class="outline">
<li><span class="review">⚑</span> <strong>The relay is a program
the site can start and stop.</strong> src/executables/TurnServer.php
builds TurnSite from settings in Config.php and is started the way the
mail server is, by CrawlDaemon. The machines screen draws a row for it
with its log and one switch, and the model says whether it is running.
What it listens on, who may use it and the ports it hands out are
TURN_BIND, TURN_PORT, TURN_REALM, TURN_USER, TURN_SECRET and
TURN_RELAY_PORTS.</li>
<li><span class="next">▶</span> <strong>A call is told where the
relay is.</strong> MessageComponent
writes GROUP_CALL_ICE_SERVERS into the page as literal JavaScript for the
browser to hand RTCPeerConnection, and a site owner writes those objects
by hand today. Where the relay is running, the site builds that entry
itself from what the relay is listening on, with the name and secret a
browser needs, so turning the relay on is all a site owner does. <ol>
<li><span class="review">⚑</span> <strong>The screen naming a
relay is drawn the way Mail Services is.</strong> The Video Call
Services box now adds a relay with the same [+] button, list and
Remove marks the mail domains use, and the controls that name one
are shown only under the two choices that hand a browser a named
relay. Its Add button had no label of its own in any language, so
the screen printed the name of the missing string; that name and a
Remove title are now written in the locale file. The wiring behind
the list had been written into the page three times over, of which
the last stood and the other two were dead, and one copy is now
kept.</li>
<li><span class="review">⚑</span> <strong>A call reaches the
site's own relay again, and a test says so.</strong> The address a
browser is handed was worked out by a call to baseUrl written in
the wrong namespace, so a site that turned its relay on died on an
undefined function. The call now names the namespace that declares
it.</li>
<li><span class="review">⚑</span> <strong>Each test case names
the calling choice it stands for.</strong> The relay cases were
written before a site owner could choose which relays a call may
use, so they read the site's own setting and failed under the
default. callRelays takes that choice as a parameter and
callServiceMode gives what a running site picked. A case for
calling turned off was added, and the whole class had been dying
silently on the fatal above.</li>
<li><span class="review">⚑</span> <strong>The fieldset carries
a mark that opens its help page.</strong> Its legend now holds the
help mark the other groups of settings have, and the page it opens,
Video Call Services, is delivered beside the patch as wiki source
for Chris to paste in.</li>
<li><span class="review">⚑</span> <strong>A saved calling
choice comes back rather than reverting.</strong> The screen read
the choice from the named settings, which hold what the settings
file said when the server started, so on a server that stays up a
saved choice showed as Disable Calling until a restart. The screen
reads the profile it already reads on every request, and the
messages screen reads the same file through the profile model, so
neither waits on a restart to see what was saved.</li>
<li><span class="review">⚑</span> <strong>The Server Settings
render methods build the link addresses they use.</strong> Splitting
render into one method per group of settings left three of them
reading $base_url, $pre_base_url, $admin_url and $token_string, which
were local to render, so under a PHP that warns on it the page
carried Undefined variable notices for the restart, clear-cache,
domain-routing and mail-log links. An adminUrls helper builds the
four from the token in $data, and each method that draws such a link
calls it, so no value is read across a method boundary.</li>
</ol></li>
<li><strong>A call is made over the relay and watched.</strong> Two
browsers driven at once, a call placed, and what each side sent and read
written down in order: the offer, the answer, the candidates, and which
of them the relay carried. That is what says whether the code as it
stands works with a relay, and it is the record a failing call is read
against. <ol>
<li><span class="review">⚑</span> <strong>The call button is
shown when calling is turned on.</strong> The button was drawn only
where the hand-set GROUP_CALL_ICE_SERVERS constant held servers, so
a site that turned calling on through the Servers screen and let its
own relay supply the servers drew no button at all. The controller
passes CALL_ENABLED, true for any mode other than off, and the
conversation header shows the controls on that.</li>
<li><span class="review">⚑</span> <strong>A call is watched
from the browser console.</strong> A call-trace logger writes each
step of a call, the channel it came over, and the seconds since the
first step, so two browsers' consoles read together show where a
call stops. It names each signaling message sent and read, each
local candidate and whether it is a relay one, and the connection
state as it changes. It is turned on for a page when the site's
debug level asks for developer messages.</li>
<li><span class="review">⚑</span> <strong>Call events ride the
WebSocket when one is open.</strong> The WebSocket push carried new
messages only, so a call's offers, answers and candidates fell to
the event stream even where a socket was open for messages. The
push now drains the waiting call events each pass and sends them in
the same envelope the browser already reads, with the event stream
left as the fall back.</li>
<li><span class="review">⚑</span> <strong>A call event is
read once and none is dropped.</strong> The time nextCallEvent
handed back came out shorter than the column held, so passed back as
the next cutoff it matched its own row again and the same event was
read twice, and a call never moved past its first answer. The time
is now written out to the microsecond. Two events timed to the same
microsecond also shared the time that is half the events' primary
key, so one was dropped; since two people in a call may write at
once under a web server that forks a process per request, the insert
is now tried and its result read, and where nothing was written the
time is moved on and the insert tried again, so a lost race is
caught rather than guessed at.</li>
</ol></li>
<li><strong>The first try is made to work.</strong> A call rings, fails,
and works when tried again. With the relay in place and a working call
written down, the failing one is reproduced against it and fixed. A call
that cannot connect says so on both screens rather than leaving them
waiting.</li>
<li><strong>Other work, grouped by what it touched.</strong> Each is a
bug Chris reported from his own site while the calling work above was
going on, and each went out as its own patch. They are gathered here by
the part of Yioop they concern rather than listed one by one. <ol>
<li><span class="review">⚑</span> <strong>The call relay was
made reachable, durable and cheap.</strong> It listened where no
browser could reach it, handed out an address the far browser could
not use, and did not come back after a restart. A second call reused
a closed connection, a call being rung timed out before anyone
picked up, and an answer built the wrong way was refused by Safari.
The relay now carries a packet without paying a disk write for
it.</li>
<li><span class="review">⚑</span> <strong>The messages screen
shows who is there and who is calling.</strong> A contact who is
signed in wears a green border, the activity pulses while a call
waits and the reader is elsewhere, and a calling contact's name
pulses and gains controls to take or turn down the call. Both pulses
stop where a reader has asked for less movement. The writing box is
dropped when no contact is chosen, and the notice that stands in its
place sits in the middle of the pane.</li>
<li><span class="review">⚑</span> <strong>Mail leaves the site,
arrives whole, and reaches the right folder.</strong> Every lookup of
where to deliver was going out malformed, so nothing sent left the
site at all. Beyond that: an attachment arrives, an uploaded file
keeps its own bytes, mail this server delivered to itself is neither
called insecure nor sent to junk, a bare name on the same site
resolves, and a message whose parts are separated by a name standing
inside a longer one, or which stops part way through a separator,
shows its words rather than a stray separator. The mail server can
check a password again and logs its heartbeat to one file.</li>
<li><span class="review">⚑</span> <strong>Wiki pages read and
edit as they should.</strong> A presentation is read as a slide deck
by all four parsers, loads the slide engine, and previews as its
slides laid out one after another; picking words in that preview
finds them where they were written. A Git page draws again, an
article page can be made, and the controls above an edit screen are
one row that says what it is, with the nav bar naming the settings,
resources and history views and the return mark going back to
editing.</li>
<li><span class="review">⚑</span> <strong>Media lists and
uploads hold up under real files.</strong> Dropping two items uploads
them, a larger upload over a secure connection finishes, a folder
lists quietly when a named file never arrived, a thumbnail inside a
folder is served rather than the source, and viewing a video reads
its figures. A group page loads and a heavier mail message is read
where memory used to run out.</li>
<li><span class="review">⚑</span> <strong>The podcast updater
downloads again and says what it decided.</strong> It was dying
before it reached a podcast at all. A forced update now names the
folders it was asked for and why it passed over each source, fetches
every item's page again rather than trusting its own record, and asks
for the folder its own page fills rather than one numbered
zero.</li>
<li><span class="review">⚑</span> <strong>Names are offered as
somebody types.</strong> The api answers a request for the names
extending what has been typed, drawn from users, groups and roles,
and the contact box on the messages screen and the role and group
boxes on Manage Users all offer them. One control stands under the
contact box where two stood. The names would not draw at first: a
warning printed ahead of the answer stopped the browser reading it,
and the box was a search box, beside which a browser built on WebKit
draws nothing from a list of choices. The two controls on the user
edit form that read Add now read as a plus, keeping the words for a
reader who cannot see them. The names are drawn by Yioop rather than
handed to the browser as a list of choices: Firefox never showed
them where Safari and Chrome did, and four attempts at finding what
Firefox wanted all failed, so the browser is no longer asked. The
three fields share one drawn list, which reads the same everywhere
and needs nothing of the browser but a place to put it. The list
falls below the box rather than over it, each name reads in bold with
what it is beneath in lighter letters and a line between one name and
the next, the letter that first reaches three asks at once while
later letters wait, and leaving the field and coming back shows the
same names again.</li>
<li><strong>A resource being edited names itself and gains
two marks.</strong> A line above the controls names the file and
opens it as a visitor reads it. A mark downloads the file, and a
file of figures gains a mark that swaps it between its rows and a
chart. The marks read the same way on a narrow screen.</li>
<li><span class="review">⚑</span> <strong>A file of a
page that lists files opens in the writing box.</strong> Opening
one drew the page's list of files instead of the file, because the
page's kind was read from where the file's settings are. The kind
now rides in a field of its own, and more kinds of file open in the
box.</li>
<li><span class="review">⚑</span> <strong>Settings, roles and
locales keep what they are told.</strong> A password requirement
turned off stays off, a modifier added to a role is kept and its
removing mark reads as a button, a locale whose settings file this
PHP will not read no longer breaks the strings screen, and the
writing mode is explained once. The memory lines say what is in use
rather than what has been claimed, and several screens build the
models they use rather than dying reaching for them.</li>
<li><span class="review">⚑</span> <strong>The Save As mark is
drawn to match its neighbors and asks for a name in a plain
dialog.</strong> The mark now wears the floppy the plain save wears,
with a small yellow pencil over its corner. It is measured at the
same box and margins as Save and the keeping mark. The dialog is a
plain browser prompt, which is how the mail screens already ask for
one name.
<ol>
<li><span class="review">⚑</span> <strong>Only the messages
screen asks whether somebody called.</strong> Every signed-in page
was asking every few seconds, which cost a steady stream of
requests. The menu no longer hands other pages the address to ask.
The messages screen alone watches for a waiting call now.</li>
<li><span class="review">⚑</span> <strong>Two dev scripts gain
the checks the last cycle showed were missing.</strong> A settle now
takes a waiting patch only when the shown message names that patch.
The devlog check counts words per added sentence and refuses one
past the limit. Both gaps had let a fault reach a delivery.</li>
<li><span class="review">⚑</span> <strong>The Save As mark is
refined and the media pencil opens the file.</strong> Save As now
shows on the grid screen too. A name with a folder mark is refused
in the dialog. A file looked at opens in the writing box from the
pencil, where its kind is one the box takes.</li>
</ol></li>
<li><span class="review">⚑</span> <strong>A picture kept with
a page is edited on a canvas in the edit screen.</strong> Opening an
image resource draws it on a canvas fitted to the screen. A row of
controls above offers the size, a quarter turn, a mirror, and drawing
tools. A save reads the canvas and writes it back over the file.
<ol>
<li><span class="review">⚑</span> <strong>The controls set
the size and turn, mirror, and draw on the picture.</strong> Width
and height fields resize it, and marks turn and mirror it. A tool
draws a line, a box, a circle, an arrow or text. A step back undoes
the last mark.</li>
<li><span class="review">⚑</span> <strong>A save writes the
canvas back over the file.</strong> The picture is sent as its own
kind and replaces the resource. A new picture is made from the file
list at a size the reader gives.</li>
<li><span class="review">⚑</span> <strong>The editor is
reached from the picture and from the pencil beside it.</strong>
Both open the same canvas, and the pencil shows only where the page
is being edited.</li>
</ol></li>
<li><span class="review">⚑</span> <strong>A cached page with
a field missing no longer warns or dies.</strong> A stored summary
without an address, a robot name, a kind, or a time set off a warning
on every read of it. Those fields now take a default. A stored page
holding something other than text killed the request at the dom
reader; it is taken only when it is text.</li>
<li><span class="review">⚑</span> <strong>The two rows of
controls on a page of files share a line.</strong> The wiki row and
the file row sit side by side where the window is wide enough. The
wiki row drops above the file row where it is not. The box holding
both is marked on such a page and lays them out.</li>
<li><span class="review">⚑</span> <strong>The index caches
answer to the memory the serving process has.</strong> They were
bounded by how many entries they held, not how large those were, so
the always-on server held over a gigabyte in them. The allowance now
takes a share of the process limit as well. The caches are given up
when the process nears that limit.</li>
</ol></li>
</ol>
</body>
</html>