Museum

Home

Lab Overview

Retrotechnology Articles

Online Manuals

⇒ xntpd(ADMN) — TCP/IP 1.2.0.i

Media Vault

Software Library

Restoration Projects

Artifacts Sought

Related Articles

xntpdc(ADMN)

ntpq(ADMN)

ntpdate(ADMN)


 xntpd(ADMN)                     19 June 1992                     xntpd(ADMN)


 Name

    xntpd - Network Time Protocol daemon


 Syntax

    xntpd [ -ab ] [ -c conffile ] [ -e authdelay ] [ -f driftfile ] [ -k key-
    file ]
    [ -r broaddelay ] [ -t trustedkey ]

 Description

    xntpd is a daemon which maintains a UNIX system's time-of-day in agree-
    ment with Internet standard time servers.  xntpd is a complete implemen-
    tation of the Network Time Protocol (NTP) version 2 standard as defined
    by RFC 1119 and also retains compatibility with version 1 servers as
    defined by RFC 1059.  xntpd does all computations in fixed point arith-
    metic and is entirely free of floating-point code.  The computations done
    in the protocol and clock adjustment code are carried out with high pre-
    cision and with attention to the details which might introduce systematic
    bias into the integrations, to try to maintain an accuracy suitable for
    synchronizing with even the most precise external time source.


    Ordinarily, xntpd reads its configuration from a file at startup time.
    The default configuration file is /etc/ntp.conf, though this may be over-
    ridden from the command line.  It is also possible to specify a working,
    though limited, xntpd configuration entirely on the command line, obviat-
    ing the need for a configuration file.  This may be particularly
    appropriate when xntpd is to be configured as a broadcast client, with
    all peers being determined by listening to broadcasts at runtime.  Vari-
    ous internal xntpd variables can be displayed, and configuration options
    altered, while the daemon is running through use of the xntpdc(ADMN) pro-
    gram.


    The following command line arguments are understood by xntpd (see the
    configuration file description for a more complete functional descrip-
    tion):


    -a   run in ``authenticate'' mode


    -b   listen for broadcast NTP and sync to this if available


    -c   specify an alternate configuration file


    -e   specify the time (in seconds) it takes to compute the NTP encryption
         field on this computer


    -f   specify the location of the drift file


    -k   specify the location of the file which contains the NTP authentica-
         tion keys


    -r   specify the default round trip delay (in seconds) to be used when
         synchronizing to broadcasts


    -t   add a key number to the trusted key list

 Configuration file options

    xntpd's configuration file is relatively free format.  Comments, which
    may be freely inserted, begin with a ``#'' character and extend to the
    end of the line.  Blank lines are ignored.  Configuration statements
    include an initial keyword followed by white space separated arguments,
    some of which may be optional.  Configuration statements may not be con-
    tinued over multiple lines.  Arguments may be network numbers (which must
    be written in numeric, dotted-quad form), integers, floating-point num-
    bers (when specifying times in seconds) and text strings.  Optional argu-
    ments are delimited by ``[]'' in the following descriptions, while alter-
    natives are separated by ``|''.

    peer hostaddress [key#] [version#] [minpoll]
    server hostaddress [key#] [version#] [minpoll]
    broadcast hostaddress [key#] [version#] [minpoll]

         These three statements specify various time servers to be used
         and/or time services to be provided.  The peer statement specifies
         that the given host is to be polled in ``symmetric active'' mode,
         that is, that the host is requested to provide time which you might
         synchronize to and, in addition, indicates that you are willing to
         have to remote host synchronize to your time if need be.  The server
         statement specifies that the given host is to be polled in
         ``client'' mode, i.e. that the host is requested to provide time
         which you might synchronize with but that you are unwilling to have
         the remote host synchronize to your own time.  The broadcast state-
         ment requests your local daemon to transmit broadcast NTP to the
         specified address.  The latter is usually the broadcast address on
         one of your local network(s).

         The key option, when included, indicates that all packets sent to
         the address are to include authentication fields encrypted using the
         specified key number (the range of which is that of an unsigned 32-
         bit integer).  The default is to not include an encryption field.
         The version option allows one to specify the version number to be
         used for outgoing NTP packets.  Versions 1 and 2 are the choices,
         version 2 is the default.  The minpoll option specifies that the
         polling interval should be kept clamped at the (64 second) minimum
         even when the local daemon isn't using the remote server's data for
         synchronization.  This is the default for broadcasting.  Otherwise,
         this option should not be specified other than for testing since it
         will increase traffic to the servers without purpose.

    precision#
         Indicates the precision of local timekeeping.  The value is an
         integer which is approximately the base 2 logarithm of the local
         timekeeping precision in seconds.  By default this value is set to
         -6.

         The precision declared by an implementation can affect several
         aspects of server operation, and can be used as a tuning parameter
         for your synchronization subnet.  It should probably not be changed
         from the default value, however, unless there is a good reason to do
         so.

    driftfile filename
         Specifies the name of the file used to record the ``drift'' (or fre-
         quency error) value xntpd has computed.  If the file exists on
         startup, it is read and the value used to initialize xntpd's inter-
         nal value of the frequency error.  The file is then updated once
         every hour by replacing the old file with a new one containing the
         current value of the frequency error.  Note that the file is updated
         by first writing the current drift value into a temporary file and
         then using rename(S) to replace the old version.  This implies that
         xntpd must have write permission for the directory the drift file is
         located in, and that file system links, symbolic or otherwise,
         should probably be avoided.

    monitor yes|no
         Indicates whether the xntpd traffic monitoring function should be
         enabled or not.  When enabled, this causes the origin address of
         each packet received by the server to be recorded along with a lim-
         ited amount of additional information, such as the mode of the
         request and whether it originated from an NTP server port or not.
         Traffic monitoring data may be inspected using the xntpdc(ADMN) mon-
         list command.  The default is ``no'', i.e. traffic monitoring should
         not be done.

         Note that the traffic monitoring facility will increase the CPU used
         by xntpd, as well as increasing the daemon's memory utilization by
         as much as 8.5 kilobytes.  This facility is normally useful for the
         detection of peers with malfunctioning software or which are sending
         bogus data.  It is primarily intended for very popular servers which
         exchange time with large numbers of peers, though it may also be
         useful for access monitoring of local servers if you are willing to
         accept the overhead.

    broadcastclient yes|no
         This indicates whether the local server should listen for, and
         attempt to synchronize to, broadcast NTP.  The default is ``no''.

    broadcastdelay seconds
         Specifies the default round trip delay to the host whose broadcasts
         are being synchronized to.  The value is specified in seconds and is
         typically (for ethernet) a number between 0.007 and 0.015 seconds.
         This initial estimate may be improved by polling each server to
         determine a more accurate value.  Defaults to 0.008 seconds.

    authenticate yes|no
         Indicates whether the local server should operate in authenticate
         mode or not.  If ``yes'', only peers which include an authentication
         field encrypted with one of our trusted keys (see below) will be
         considered as candidates for synchronizing to.  The default is
         ``no''.

    authdelay seconds
         Indicates the amount of time it takes to encrypt an NTP authentica-
         tion field on the local computer.  This value is used to correct
         transmit timestamps when the authentication is used on outgoing
         packets.  The value usually lies somewhere in the range 0.0001
         seconds to 0.003 seconds, though it is very dependent on the CPU
         speed of the host computer.  The value is usually computed using the
         authspeed program included with the distribution.

    keys filename
         Specifies the name of a file which contains the encryption keys
         which are to be used by xntpd.  The format of this file is described
         below.

    trustedkey # [# ...]
         Allows the specification of the encryption key numbers which are
         trusted for the purposes of determining peers suitable for time
         sychronization, when authentication is enabled.  Only peers using
         one of these keys for encryption of the authentication field, and
         whose authenticity can be verified by successful decryption, will be
         considered as synchronization candidates.  The arguments are 32-bit
         unsigned integers.  Note, however, that NTP key 0 is fixed and glo-
         bally known. If meaningful authentication is to be performed, the 0
         key should not be trusted.

    requestkey #
         xntpd allows runtime reconfiguration to be performed using the
         xntpdc(ADMN) program.  Such requests must be authenticated.  The
         requestkey statement allows the specification of a 32-bit unsigned
         integer key number to be used for authenticating such requests.
         Note that if no requestkey statement is included in the configura-
         tion file the runtime reconfiguration facility will be disabled.

    controlkey #
         Certain changes can be made to the xntpd server via mode 6 control
         messages, in particular the setting of leap second indications in a
         server with a radio clock.  The controlkey statement specifies an
         encription key number to be used for authenticating such messages.
         Omitting this statement will cause control messages which would
         change the state of the server to be ignored.

    restrict address [mask numericmask] [flag] [...]
         xntpd implements a general purpose address-and-mask based restric-
         tion list.  The list is sorted by address and by mask, and the list
         is searched in this order for matches, with the last match found
         defining the restriction flags associated with the incoming packets.
         The source address of incoming packets is used for the match, with
         the 32-bit address being and'ed with the mask associated with the
         restriction entry and then compared with the entry's address (which
         has also been and'ed with the mask) to look for a match.  The
         ``mask'' argument defaults to 255.255.255.255, meaning that the
         ``address'' is treated as the address of an individual host.  A
         default entry (address 0.0.0.0, mask 0.0.0.0) is always included
         and, given the sort algorithm, is always the first entry in the
         list.  Note that, while ``address'' is normally given as a
         dotted-quad address, the text string ``default'', with no mask
         option, may be used to indicate the default entry.

         In the current implementation, flags always restrict access, i.e. an
         entry with no flags indicates that free access to the server is to
         be given.  The flags are not orthogonal, in that more restrictive
         flags will often make less restrictive ones redundant.  The flags
         can generally be classed into two categories, those which restrict
         time service and those which restrict informational queries and
         attempts to do runtime reconfiguration of the server.  One or more
         of the following flags may be specified:

         ignore      Ignore all packets from hosts which match this entry.
                     If this flag is specified, neither queries nor time
                     server polls will be responded to.

         noquery     Ignore all NTP mode 6 and 7 packets (that is, informa-
                     tion queries and configuration requests) from the
                     source.  Time service is not affected.

         nomodify    Ignore all NTP mode 6 and 7 packets which attempt to
                     modify the state of the server (that is, runtime recon-
                     figuration).  Queries which return information are per-
                     mitted.

         notrap      Decline to provide mode 6 control message trap service
                     to matching hosts.  The trap service is a subsystem of
                     the mode 6 control message protocol which is intended
                     for use by remote event logging programs.

         lowpriotrap Declare traps set by matching hosts to be low priority.
                     The number of traps a server can maintain is limited
                     (the current limit is 3).  Traps are usually assigned on
                     a first-come, first-served basis, with later trap
                     requestors being denied service.  This flag modifies the
                     assignment algorithm by allowing low priority traps to
                     be overridden by later requests for normal priority
                     traps.

         noserve     Ignore NTP packets whose mode is other than 6 or 7. In
                     effect, time service is denied, though queries may still
                     be permitted.

         nopeer      Provide stateless time service to polling hosts, but do
                     not allocate peer memory resources to these hosts even
                     if they otherwise might be considered useful as future
                     synchronization partners.

         notrust     Treat these hosts normally in other respects, but never
                     use them as synchronization sources.

         ntpport     This is actually a match algorithm modifier, rather than
                     a restriction flag.  Its presence causes the restriction
                     entry to be matched only if the source port in the
                     packet is the standard NTP UDP port (123).  Both
                     ``ntpport'' and non-``ntpport'' may be specified.  The
                     ``ntpport'' is considered more specific and is sorted
                     later in the list.

         Default restriction list entries, with the flags ``ignore,
         ntpport'', for each of the local host's interface addresses are
         inserted into the table at startup to prevent the server from
         attempting to synchronize to its own time.  A default entry is also
         always present, though if it is otherwise unconfigured no flags are
         associated with the default entry (that is, everything besides your
         own NTP server is unrestricted).

         The restriction facility was added to allow the current access poli-
         cies of the time servers running on the NSFnet backbone to be imple-
         mented with xntpd as well.  While this facility may be otherwise
         useful for keeping unwanted or broken remote time servers from
         affecting your own, it should not be considered an alternative to
         the standard NTP authentication facility.  Source address based re-
         strictions are easily circumvented by a determined cracker.

    trap hostaddress [port
    portnumber] [interface interfaceaddress]
         Configures a trap receiver at the given host address and port num-
         ber, sending messages with the specified local interface address.
         If the port number is unspecified, a value of 18447 is used.  If the
         interface address is not specified, the message is sent with a
         source address which is that of the local interface the message is
         sent through.  Note that on a multihomed host the interface used may
         vary from time to time with routing changes.

         The trap receiver will generally log event messages and other infor-
         mation from the server in a log file.  While such monitor programs
         may also request their own trap dynamically, configuring a trap
         receiver will ensure that no messages are lost when the server is
         started.

    maxskew seconds
         Sets the system maximum skew parameter to the number of seconds
         given.  The default value is 0.010 seconds.  This is a tuning param-
         eter of use in improving performance when network link conditions
         are poor, and should probably not be changed unless your server is
         to run under exceptional conditions.

    select algorithmnumber
         Selects the use of one of five selection weight algorithms.  The
         default is algorithm number 1, which is the algorithm specified in
         RFC 1119.  Algorithm numbers 2 through 5 select alternative, experi-
         mental selection weighting algorithms, all of which tend to give a
         greater degree of trust to either lower stratum and/or lower delay
         peers than the standard algorithm.

    resolver /path/xntpres
         Indicates to the daemon the full path to the xntpres program.  This
         utility is used when names requiring resolution (rather than numeric
         addresses) are used in peer and server entries in the configuration
         file.  As xntpres makes use of mode 7 runtime reconfiguration this
         facility must also be enabled if the procedure is to exceed (see the
         requestkey and keys statements above).


 Authentication key file format

    The NTP standard specifies an extension allowing verification of the
    authenticity of received NTP packets, and to provide an indication of
    authenticity in outgoing packets.  This is implemented in xntpd using the
    DES encryption algorithm.  The specification allows any one of a possible
    4 billion keys, numbered with 32-bit unsigned integers, to be used to
    authenticate an association.  The servers involved in an association must
    agree on the value of the key used to authenticate their data, though
    they must each learn the key independently.  The keys are standard 56-bit
    DES keys.

    xntpd reads its keys from a file specified using the -k command line
    option or the keys statement in the configuration file.  While key number
    0 is fixed by the NTP standard (as 56 zero bits) and may not be changed,
    one or more of the keys numbered 1 through 15 may be arbitrarily set in
    the keys file.

    The key file uses the same comment conventions as the configuration file.
    Key entries use a fixed format of the form

         keyno  type  key

    where keyno is a positive integer, type is a single character which
    defines the format the key is given in, and key is the key itself.

    The key may be given in one of three different formats, controlled by the
    type character.  The three key types, and corresponding formats, are
    listed following.

    S    The key is a 64-bit hexadecimal number in the format specified in
         the DES document, that is the high order 7 bits of each octet are
         used to form the 56-bit key while the low order bit of each octet is
         given a value such that odd parity is maintained for the octet.
         Leading zeroes must be specified (that is, the key must be exactly
         16 hex digits long) and odd parity must be maintained.  Hence a zero
         key, in standard format, would be given as 0101010101010101.

    N    The ``key'' is a 64-bit hexadecimal number in the format specified
         in the NTP standard.  This is the same as the DES format except the
         bits in each octet have been rotated one bit right so that the par-
         ity bit is now the high order bit of the octet.  Leading zeroes must
         be specified and odd parity must be maintained.  A zero key in NTP
         format would be specified as 8080808080808080

    A    The ``key'' is a 1-to-8 character ASCII string.  A key is formed
         from this by using the lower order 7 bits of the ASCII representa-
         tion of each character in the string, with zeroes being added on the
         right when necessary to form a full width 56-bit key, in the same
         way that encryption keys are formed from UNIX passwords.

    One of the keys may be chosen, by way of the configuration file request-
    key statement, to authenticate run time configuration requests made using
    the xntpdc(ADMN) program.  The latter program obtains the key from the
    terminal as a password, so it is generally appropriate to specify the key
    chosen to be used for this purpose in ASCII format.

 Primary clock support

    xntpd can be optionally compiled to include support for a number of types
    of reference clocks.  A reference clock will generally (though not
    always) be a radio timecode receiver which is synchronized to a source of
    standard time such as the services offered by the NRC in Canada and NIST
    in the U.S.  The interface between the computer and the timecode receiver
    is device dependent and will vary, but is often a serial port.

    For the purposes of configuration, xntpd treats reference clocks in a
    manner analogous to normal NTP peers as much as possible.  Reference
    clocks are referred to by address, much as a normal peer is, though an
    invalid IP address is used to distinguish them from normal peers.  Refer-
    ence clock addresses are of the form 127.127.t.u where t is an integer
    denoting the clock type and u indicates the type-specific unit number.
    Reference clocks are normally enabled by configuring the clock as a
    server using a server statement in the configuration file which refer-
    ences the clock's address (configuring a reference clock with a peer
    statement can also be done, though with some clock drivers this may cause
    the clock to be treated somewhat differently and by convention is used
    for debugging purposes).  Clock addresses may generally be used anywhere
    else in the configuration file a normal IP address can be used, for exam-
    ple in restrict statements.

    There is one additional configuration statement which becomes valid when
    reference clock support has been compiled in.  Its format is:


    fudge 127.127.t.u [time1 secs] [time2 secs] [value1 int] [value2 int]
         [flag1 0|1] [flag2 0|1]
         There are two times (whose values are specified in fixed point
         seconds), two integral values and two binary flags, available for
         customizing the operation of a clock.  The interpretation of these
         values, and whether they are used at all, is a function of the needs
         of the particular clock driver.

         xntpd on UNIX machines currently supports three different types of
         clock hardware plus a special pseudo-clock used for backup or when
         no other clock source is available.  The clock drivers, and the
         addresses used to configure them, are described following:

    127.127.1.u - Local synchronization clock driver
         This driver doesn't support an actual clock, but rather allows the
         server to synchronize to its own clock, in essence to free run
         without its stratum increasing to infinity.  This can be used to run
         an isolated NTP synchronization network where no standard time
         source is available, by allowing a free running clock to appear as
         if it has external synchronization to other servers.  By running the
         local clock at an elevated stratum it can also be used to prevent a
         server's stratum from rising above a fixed value, this allowing a
         synchronization subnet to synchronize to a single local server for
         periods when connectivity to the primary servers is lost.

         The unit number of the clock (the least significant octet in the
         address) must lie in the range 0 through 15 inclusive and is used as
         the stratum the local clock will run at.  Note that the server, when
         synchronized to the local clock, will advertise a stratum one
         greater than the clock peer's stratum.  More than one local clock
         may be configured (indeed all 16 units may be active at once),
         though this hardly seems useful.

         The local clock driver uses only the fudge time1 parameter.  This
         parameter actually provides read and write access to the local clock
         drift compensation register.  This value, which actually provides a
         fine resolution speed adjustment for the local clock, is settable
         but will remain unchanged from any set value when the clock is free
         running without external synchronization.  The fudge time1 parameter
         thus provides a way to manually adjust the speed of the clock to
         maintain reasonable synchronization with, say, a voice time
         announcement.  It is actually more useful to manipulate this value
         with the xntpdc(ADMN) program.

    127.127.3.u - Precision Standard Time 1010/1020 WWV/H Receiver
         This driver can be used to receive time from a PST 1020 WWV receiver
         connected to a serial port on the computer.  Any or all of four
         units, with unit numbers in the range 0 through 3, can be config-
         ured.  The driver assumes the serial line the clock is connected to
         is /dev/pst%d (that is, unit 1, at 127.127.3.1, opens /dev/pst1 to
         speak to the clock) and that the PST receiver is configured for 9600
         baud operation.

         The fudge time1 and time2 parameters are used by the driver as nomi-
         nal propagation delays when synchronized to WWV and WWVH, respec-
         tively.  They default to 0.0075 and 0.0265 seconds, values which are
         about right for Toronto.  While this should in principle be set to
         the propagation delays appropriate for the location, one should not
         feel inhibited from tweaking the values to make the output of the
         clock match other stratum 1NTP peers more exactly.  The only con-
         sideration when adjusting these is that the difference between the
         values should be kept set to the predicted difference in propagation
         delay between WWV and WWVH at all times.

         The value1 parameter can be used to set the stratum at which the
         peer operates.  The default is 0, which is correct if you want the
         clock to be considered for synchronization whenever it is operating,
         though higher values may be assigned if you only want the clock to
         provide backup service when all other primary sources have failed.
         The value2 parameter is set to the number of minutes which the dae-
         mon will allow the clock to go without synchronization before it
         starts disbelieving it.  The default is 20, which is suitable if you
         have good quality backup NTP peers.  If your network is isolated or
         your network connections are poor it might be advantageous to
         increase this value substantially.

         The fudge flag1 can be set with good effect if your system clock's
         precision is worse than about 500 microseconds, as it causes the
         code processing algorithms to be modified slightly in a way which
         should produce better results with imprecise clocks.  Setting fudge
         flag2 will force the driver to send to the clock the commands
         required to program the current WWV and WWVH fudge delays into it
         (this is normally done only when the values change).  Setting the
         (otherwise undocumented) fudge flag3 will cause the driver to reset
         the clock.  The latter two flags are generally only useful for
         debugging.

    127.127.4.u - Spectracom 8170 WWVB Receiver
         This driver provides an interface to the Spectracom 8170 WWVB
         receiver.  The driver requires two outputs from the receiver.  The
         normal RS232 output is assumed configured for 9600 bps operation,
         while the PPS output must be run through a TTL-to-RS232 serial con-
         verter which produces a valid character (with the start bit syn-
         chronized to on time) at 19200 bps.  The driver opens the RS232 out-
         put on /dev/wwvb%d and the PPS output on /dev/wwvbpps%d, where %d is
         replaced by the unit number of the unit opened.

         The fudge time1 parameter is used as a general calibration factor
         for the clock, with positive values advancing the time and negative
         values retarding it.  The parameter defaults to zero, which should
         be appropriate if the clock's propagation delay switches have been
         set appropriately.  The value1 parameter can be used to set the
         stratum at which the peer operates.  The default is 0, which is
         correct if you want the clock to be considered for synchronization
         whenever it is operating.  The value2 parameter is set to the
         decimal equivalent of the character which is to be received on the
         PPS port.  It defaults to 240 (hex 0xf0), which is the character
         received when a 260 microsecond pulse is provided to the PPS port
         synchronized to the on time second.

         The fudge flag1 can be set with good effect if your system clock's
         precision is worse than about 500 microseconds, as it causes the
         code processing algorithms to be modified slightly in a way which
         should produce better results with imprecise clocks.  Setting fudge
         flag2 will force the driver to clear a leap second hold condition.
         The driver will mark the receiver unsynchronized for a period of 45
         minutes after a leap second occurs to make sure the clock resyn-
         chronizes.  As the clock may actually resynchronize within a few
         minutes of the event, the flag2 parameter provides a way to force
         the daemon to resynchronize to the clock manually.

    127.127.7.u - Direct synchronization to the CHU timecode
         Unlike the NIST time services, whose timecode requires quite spe-
         cialized hardware to interpret, the CHU timecode can be received
         directly via a serial port after demodulation.  While there are
         currently no commercial CHU receivers the hardware required to
         receive the CHU timecode is fairly simple to build.  The driver sup-
         ports four units, numbered 0 through 3, and opens /dev/chu%d to
         access the serial device the particular unit is connected to.  While
         it is possible to configure several CHU units simultaneously this is
         not recommended as the character interrupts from all units will be
         occurring at the same time and will interfere with each other.  Note
         that the operation of the CHU driver (and probably the ability to
         actually compile the driver into the daemon) requires that a special
         purpose CHU serial line discipline be installed in your kernel to
         take timestamps at interrupt level and to do noise prefiltering.

         The CHU driver uses fudge time1 and time2 as calibration factors.
         By convention, time1 is the estimated propagation delay to CHU (the
         propdelay program in the distribution may be used to compute a rough
         estimate of this) while time2 is an estimate of
         propagation-independent delays through the modem filters and serial
         driver.  These values default to 0.0025 and 0.0002 seconds, respec-
         tively, values which are suitable for Toronto and the modem I built.
         Fudge value1 can be set to the stratum you wish the clock peer to
         operate at.  This defaults to zero, though a higher stratum can be
         set if the clock is only to be used for backup.  If fudge flag1 is
         set a slightly different algorithm, better suited for machines where
         the system clock precision is coarser than about 500 microseconds,
         is selected for processing the raw data.


 Files


    /etc/ntp.conf  the default name of the configuration file

    /etc/ntp.drift the conventional name of the drift file

    /etc/ntp.keys  the conventional name of the key file


 See also

    xntpdc(ADMN), ntpq(ADMN), ntpdate(ADMN), RFC 1059, RFC 1119, RFC 1128,
    RFC 1129


Typewritten Software • bear@typewritten.org • Edmonds, WA 98026