<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Phase 8 &mdash; Calling &mdash; 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 &mdash; Calling</h1>
<p><em>Markers: <span class="next">&#9654;</span> being worked on,
<span class="review">&#9873;</span> implemented and waiting to be
reviewed,
<span class="check">&#10003;</span> done,
<span class="check">&#10003;</span><span class="nope">&#10007;</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">&#9873;</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">&#9654;</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">&#9873;</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">&#9873;</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">&#9873;</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">&#9873;</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">&#9873;</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">&#9873;</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">&#9873;</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">&#9873;</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">&#9873;</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">&#9873;</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">&#9873;</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">&#9873;</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">&#9873;</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">&#9873;</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">&#9873;</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">&#9873;</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">&#9873;</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">&#9873;</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">&#9873;</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">&#9873;</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">&#9873;</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">&#9873;</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">&#9873;</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">&#9873;</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">&#9873;</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">&#9873;</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">&#9873;</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">&#9873;</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">&#9873;</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">&#9873;</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>
X